@pikku/core 0.12.24 → 0.12.26
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/CHANGELOG.md +24 -0
- package/dist/data-classification.d.ts +16 -0
- package/dist/data-classification.js +1 -0
- package/dist/index.d.ts +1 -0
- package/dist/services/gopass-secrets.d.ts +2 -35
- package/dist/services/gopass-secrets.js +10 -40
- package/dist/services/local-secrets.d.ts +2 -36
- package/dist/services/local-secrets.js +3 -52
- package/dist/services/local-variables.d.ts +3 -5
- package/dist/services/local-variables.js +10 -10
- package/dist/services/scoped-secret-service.d.ts +2 -3
- package/dist/services/scoped-secret-service.js +2 -6
- package/dist/services/secret-service.d.ts +5 -12
- package/dist/services/typed-secret-service.d.ts +3 -4
- package/dist/services/typed-secret-service.js +2 -5
- package/dist/services/typed-variables-service.d.ts +3 -6
- package/dist/services/typed-variables-service.js +0 -6
- package/dist/services/variables-service.d.ts +2 -4
- package/dist/testing/service-tests.js +13 -13
- package/dist/wirings/ai-agent/ai-agent-prepare.js +4 -1
- package/dist/wirings/ai-agent/ai-agent.types.d.ts +4 -0
- package/dist/wirings/oauth2/oauth2-client.js +3 -3
- package/package.json +1 -1
- package/src/data-classification.ts +15 -0
- package/src/index.ts +9 -0
- package/src/services/gopass-secrets.ts +10 -42
- package/src/services/local-secrets.test.ts +10 -10
- package/src/services/local-secrets.ts +11 -59
- package/src/services/local-variables.test.ts +4 -4
- package/src/services/local-variables.ts +12 -17
- package/src/services/scoped-secret-service.test.ts +10 -11
- package/src/services/scoped-secret-service.ts +4 -9
- package/src/services/secret-service.ts +5 -12
- package/src/services/typed-secret-service.test.ts +16 -17
- package/src/services/typed-secret-service.ts +5 -9
- package/src/services/typed-variables-service.test.ts +5 -5
- package/src/services/typed-variables-service.ts +5 -17
- package/src/services/variables-service.ts +2 -4
- package/src/testing/service-tests.ts +13 -13
- package/src/wirings/ai-agent/ai-agent-prepare.ts +7 -2
- package/src/wirings/ai-agent/ai-agent.types.ts +4 -0
- package/src/wirings/oauth2/oauth2-client.test.ts +2 -3
- package/src/wirings/oauth2/oauth2-client.ts +3 -3
- package/tsconfig.tsbuildinfo +1 -1
package/CHANGELOG.md
CHANGED
|
@@ -1,3 +1,27 @@
|
|
|
1
|
+
## 0.12.26
|
|
2
|
+
|
|
3
|
+
### Patch Changes
|
|
4
|
+
|
|
5
|
+
- 909eb25: Add audit logging support for function invocations and database queries.
|
|
6
|
+
|
|
7
|
+
Introduces `AuditService` and `createAuditedKysely` — configurable audit capture with best-effort and transactional durability modes. Audit logs capture session metadata (user, org), RPC call details, and Kysely query operations (type, tables, changes). Audit context is scoped per-invocation so nested RPC calls are correctly attributed.
|
|
8
|
+
|
|
9
|
+
## 0.12.25
|
|
10
|
+
|
|
11
|
+
### Patch Changes
|
|
12
|
+
|
|
13
|
+
- 665bdb0: Add end-to-end data classification for SQLite and Postgres projects.
|
|
14
|
+
|
|
15
|
+
**Core (`@pikku/core`):** New `Private<T>` and `Secret<T>` intersection brands, `ClassificationManifest`, `ColumnClassification`, and `AnonymizeStrategy` types exported from `data-classification.ts`.
|
|
16
|
+
|
|
17
|
+
**CLI (`@pikku/cli`):**
|
|
18
|
+
- SQL comment annotations: `-- @public`, `-- @private[:strategy]`, `-- @secret[:strategy]` on `CREATE TABLE` columns and `ALTER TABLE ... ADD COLUMN` statements. Unannotated columns default to `private`.
|
|
19
|
+
- `pikku db migrate` now emits a `classification.gen.ts` manifest alongside `schema.d.ts`.
|
|
20
|
+
- New `pikku db audit` command — prints a per-column classification summary and warns on `private`/`secret` columns with no anonymize strategy.
|
|
21
|
+
- Postgres dialect support in `resolveDb`, `PostgresMigrationExecutor`, and `PostgresIntrospector`.
|
|
22
|
+
|
|
23
|
+
**Inspector (`@pikku/inspector`):** New PKU910 check — `findPiiPaths()` walks inferred function return types looking for `__pii__` brands (including inside `Array<T>`, `Record<K,V>`, and index signatures) and fails the build if a function exposes branded fields in its output.
|
|
24
|
+
|
|
1
25
|
## 0.12.24
|
|
2
26
|
|
|
3
27
|
### Patch Changes
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
export type Private<T> = T & {
|
|
2
|
+
readonly __pii__: 'private';
|
|
3
|
+
};
|
|
4
|
+
export type Secret<T> = T & {
|
|
5
|
+
readonly __pii__: 'secret';
|
|
6
|
+
};
|
|
7
|
+
export type Classification = 'public' | 'private' | 'secret';
|
|
8
|
+
export type AnonymizeStrategy = 'fake:email' | 'fake:name' | 'hash' | 'keep' | null;
|
|
9
|
+
export interface ColumnClassification {
|
|
10
|
+
classification: Classification;
|
|
11
|
+
anonymize_strategy: AnonymizeStrategy;
|
|
12
|
+
}
|
|
13
|
+
export type ClassificationManifest = {
|
|
14
|
+
version: 1;
|
|
15
|
+
tables: Record<string, Record<string, ColumnClassification>>;
|
|
16
|
+
};
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export {};
|
package/dist/index.d.ts
CHANGED
|
@@ -50,3 +50,4 @@ export { isSerializable, stopSingletonServices } from './utils.js';
|
|
|
50
50
|
export { getSingletonServices, getCreateWireServices } from './pikku-state.js';
|
|
51
51
|
export { type ScheduledTaskInfo, type ScheduledTaskSummary, } from './services/scheduler-service.js';
|
|
52
52
|
export { SchedulerService } from './services/scheduler-service.js';
|
|
53
|
+
export type { Private, Secret, Classification, AnonymizeStrategy, ColumnClassification, ClassificationManifest, } from './data-classification.js';
|
|
@@ -5,43 +5,10 @@ import type { SecretService } from './secret-service.js';
|
|
|
5
5
|
*/
|
|
6
6
|
export declare class GopassSecretService implements SecretService {
|
|
7
7
|
private prefix;
|
|
8
|
-
/**
|
|
9
|
-
* Creates an instance of GopassSecretService.
|
|
10
|
-
* @param prefix - Optional prefix for all secret keys (e.g., 'pikku/' will look up 'pikku/mykey' for key 'mykey')
|
|
11
|
-
*/
|
|
12
8
|
constructor(prefix?: string);
|
|
13
9
|
private getFullKey;
|
|
14
|
-
|
|
15
|
-
* Retrieves a secret by key and parses it as JSON.
|
|
16
|
-
* @param key - The key of the secret to retrieve.
|
|
17
|
-
* @returns A promise that resolves to the parsed secret value.
|
|
18
|
-
* @throws {Error} If the secret is not found or gopass fails.
|
|
19
|
-
*/
|
|
20
|
-
getSecretJSON<R>(key: string): Promise<R>;
|
|
21
|
-
/**
|
|
22
|
-
* Retrieves a secret by key as a string.
|
|
23
|
-
* @param key - The key of the secret to retrieve.
|
|
24
|
-
* @returns A promise that resolves to the secret value.
|
|
25
|
-
* @throws {Error} If the secret is not found or gopass fails.
|
|
26
|
-
*/
|
|
27
|
-
getSecret(key: string): Promise<string>;
|
|
28
|
-
/**
|
|
29
|
-
* Checks if a secret exists without throwing.
|
|
30
|
-
* @param key - The key of the secret to check.
|
|
31
|
-
* @returns A promise that resolves to true if the secret exists.
|
|
32
|
-
*/
|
|
10
|
+
getSecret<T = string>(key: string): Promise<T>;
|
|
33
11
|
hasSecret(key: string): Promise<boolean>;
|
|
34
|
-
|
|
35
|
-
* Stores a JSON value as a secret in gopass.
|
|
36
|
-
* @param key - The key to store the secret under.
|
|
37
|
-
* @param value - The JSON value to store.
|
|
38
|
-
* @returns A promise that resolves when the secret is stored.
|
|
39
|
-
*/
|
|
40
|
-
setSecretJSON(key: string, value: unknown): Promise<void>;
|
|
41
|
-
/**
|
|
42
|
-
* Deletes a secret from gopass.
|
|
43
|
-
* @param key - The key of the secret to delete.
|
|
44
|
-
* @returns A promise that resolves when the secret is deleted.
|
|
45
|
-
*/
|
|
12
|
+
setSecret(key: string, value: unknown): Promise<void>;
|
|
46
13
|
deleteSecret(key: string): Promise<void>;
|
|
47
14
|
}
|
|
@@ -5,10 +5,6 @@ import { execFileSync } from 'child_process';
|
|
|
5
5
|
*/
|
|
6
6
|
export class GopassSecretService {
|
|
7
7
|
prefix;
|
|
8
|
-
/**
|
|
9
|
-
* Creates an instance of GopassSecretService.
|
|
10
|
-
* @param prefix - Optional prefix for all secret keys (e.g., 'pikku/' will look up 'pikku/mykey' for key 'mykey')
|
|
11
|
-
*/
|
|
12
8
|
constructor(prefix = '') {
|
|
13
9
|
this.prefix = prefix;
|
|
14
10
|
}
|
|
@@ -18,38 +14,23 @@ export class GopassSecretService {
|
|
|
18
14
|
}
|
|
19
15
|
return this.prefix ? `${this.prefix}${key}` : key;
|
|
20
16
|
}
|
|
21
|
-
/**
|
|
22
|
-
* Retrieves a secret by key and parses it as JSON.
|
|
23
|
-
* @param key - The key of the secret to retrieve.
|
|
24
|
-
* @returns A promise that resolves to the parsed secret value.
|
|
25
|
-
* @throws {Error} If the secret is not found or gopass fails.
|
|
26
|
-
*/
|
|
27
|
-
async getSecretJSON(key) {
|
|
28
|
-
const value = await this.getSecret(key);
|
|
29
|
-
return JSON.parse(value);
|
|
30
|
-
}
|
|
31
|
-
/**
|
|
32
|
-
* Retrieves a secret by key as a string.
|
|
33
|
-
* @param key - The key of the secret to retrieve.
|
|
34
|
-
* @returns A promise that resolves to the secret value.
|
|
35
|
-
* @throws {Error} If the secret is not found or gopass fails.
|
|
36
|
-
*/
|
|
37
17
|
async getSecret(key) {
|
|
38
18
|
const fullKey = this.getFullKey(key);
|
|
39
19
|
try {
|
|
40
|
-
|
|
20
|
+
const raw = execFileSync('gopass', ['show', '-o', fullKey], {
|
|
41
21
|
encoding: 'utf8',
|
|
42
22
|
}).trim();
|
|
23
|
+
try {
|
|
24
|
+
return JSON.parse(raw);
|
|
25
|
+
}
|
|
26
|
+
catch {
|
|
27
|
+
return raw;
|
|
28
|
+
}
|
|
43
29
|
}
|
|
44
30
|
catch (error) {
|
|
45
31
|
throw new Error(`Secret Not Found: ${key}`, { cause: error });
|
|
46
32
|
}
|
|
47
33
|
}
|
|
48
|
-
/**
|
|
49
|
-
* Checks if a secret exists without throwing.
|
|
50
|
-
* @param key - The key of the secret to check.
|
|
51
|
-
* @returns A promise that resolves to true if the secret exists.
|
|
52
|
-
*/
|
|
53
34
|
async hasSecret(key) {
|
|
54
35
|
const fullKey = this.getFullKey(key);
|
|
55
36
|
try {
|
|
@@ -60,19 +41,13 @@ export class GopassSecretService {
|
|
|
60
41
|
return false;
|
|
61
42
|
}
|
|
62
43
|
}
|
|
63
|
-
|
|
64
|
-
* Stores a JSON value as a secret in gopass.
|
|
65
|
-
* @param key - The key to store the secret under.
|
|
66
|
-
* @param value - The JSON value to store.
|
|
67
|
-
* @returns A promise that resolves when the secret is stored.
|
|
68
|
-
*/
|
|
69
|
-
async setSecretJSON(key, value) {
|
|
44
|
+
async setSecret(key, value) {
|
|
70
45
|
const fullKey = this.getFullKey(key);
|
|
71
|
-
const
|
|
46
|
+
const encoded = typeof value === 'string' ? value : JSON.stringify(value);
|
|
72
47
|
try {
|
|
73
48
|
execFileSync('gopass', ['insert', '-f', fullKey], {
|
|
74
49
|
encoding: 'utf8',
|
|
75
|
-
input:
|
|
50
|
+
input: encoded,
|
|
76
51
|
stdio: ['pipe', 'pipe', 'pipe'],
|
|
77
52
|
});
|
|
78
53
|
}
|
|
@@ -80,11 +55,6 @@ export class GopassSecretService {
|
|
|
80
55
|
throw new Error(`Failed to set secret: ${key}`, { cause: error });
|
|
81
56
|
}
|
|
82
57
|
}
|
|
83
|
-
/**
|
|
84
|
-
* Deletes a secret from gopass.
|
|
85
|
-
* @param key - The key of the secret to delete.
|
|
86
|
-
* @returns A promise that resolves when the secret is deleted.
|
|
87
|
-
*/
|
|
88
58
|
async deleteSecret(key) {
|
|
89
59
|
const fullKey = this.getFullKey(key);
|
|
90
60
|
try {
|
|
@@ -8,43 +8,9 @@ export declare class LocalSecretService implements SecretService {
|
|
|
8
8
|
private variables;
|
|
9
9
|
private localSecrets;
|
|
10
10
|
private parseSecret;
|
|
11
|
-
/**
|
|
12
|
-
* Creates an instance of LocalSecretService.
|
|
13
|
-
*/
|
|
14
11
|
constructor(variables?: VariablesService);
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
* Checks local storage first, then falls back to environment variables.
|
|
18
|
-
* @param key - The key of the secret to retrieve.
|
|
19
|
-
* @returns A promise that resolves to the secret value.
|
|
20
|
-
* @throws {Error} If the secret is not found.
|
|
21
|
-
*/
|
|
22
|
-
getSecretJSON<R>(key: string): Promise<R>;
|
|
23
|
-
/**
|
|
24
|
-
* Retrieves a secret by key.
|
|
25
|
-
* Checks local storage first, then falls back to environment variables.
|
|
26
|
-
* @param key - The key of the secret to retrieve.
|
|
27
|
-
* @returns A promise that resolves to the secret value.
|
|
28
|
-
* @throws {Error} If the secret is not found.
|
|
29
|
-
*/
|
|
30
|
-
getSecret(key: string): Promise<string>;
|
|
31
|
-
/**
|
|
32
|
-
* Stores a JSON value as a secret in local storage.
|
|
33
|
-
* @param key - The key to store the secret under.
|
|
34
|
-
* @param value - The JSON value to store.
|
|
35
|
-
* @returns A promise that resolves when the secret is stored.
|
|
36
|
-
*/
|
|
37
|
-
setSecretJSON(key: string, value: unknown): Promise<void>;
|
|
38
|
-
/**
|
|
39
|
-
* Checks if a secret exists without throwing.
|
|
40
|
-
* @param key - The key of the secret to check.
|
|
41
|
-
* @returns A promise that resolves to true if the secret exists.
|
|
42
|
-
*/
|
|
12
|
+
getSecret<T = string>(key: string): Promise<T>;
|
|
13
|
+
setSecret(key: string, value: unknown): Promise<void>;
|
|
43
14
|
hasSecret(key: string): Promise<boolean>;
|
|
44
|
-
/**
|
|
45
|
-
* Deletes a secret from local storage.
|
|
46
|
-
* @param key - The key of the secret to delete.
|
|
47
|
-
* @returns A promise that resolves when the secret is deleted.
|
|
48
|
-
*/
|
|
49
15
|
deleteSecret(key: string): Promise<void>;
|
|
50
16
|
}
|
|
@@ -6,7 +6,6 @@ import { LocalVariablesService } from './local-variables.js';
|
|
|
6
6
|
export class LocalSecretService {
|
|
7
7
|
variables;
|
|
8
8
|
localSecrets = new Map();
|
|
9
|
-
// TODO: Drop this fallback once secrets and variables expose aligned typed/raw access paths again.
|
|
10
9
|
parseSecret(raw) {
|
|
11
10
|
try {
|
|
12
11
|
return JSON.parse(raw);
|
|
@@ -15,66 +14,23 @@ export class LocalSecretService {
|
|
|
15
14
|
return raw;
|
|
16
15
|
}
|
|
17
16
|
}
|
|
18
|
-
/**
|
|
19
|
-
* Creates an instance of LocalSecretService.
|
|
20
|
-
*/
|
|
21
17
|
constructor(variables = new LocalVariablesService()) {
|
|
22
18
|
this.variables = variables;
|
|
23
19
|
}
|
|
24
|
-
|
|
25
|
-
* Retrieves a secret by key.
|
|
26
|
-
* Checks local storage first, then falls back to environment variables.
|
|
27
|
-
* @param key - The key of the secret to retrieve.
|
|
28
|
-
* @returns A promise that resolves to the secret value.
|
|
29
|
-
* @throws {Error} If the secret is not found.
|
|
30
|
-
*/
|
|
31
|
-
async getSecretJSON(key) {
|
|
32
|
-
// Check local storage first
|
|
20
|
+
async getSecret(key) {
|
|
33
21
|
const localValue = this.localSecrets.get(key);
|
|
34
22
|
if (localValue) {
|
|
35
23
|
return this.parseSecret(localValue);
|
|
36
24
|
}
|
|
37
|
-
// Fall back to environment variables
|
|
38
25
|
const value = await this.variables.get(key);
|
|
39
26
|
if (value) {
|
|
40
27
|
return this.parseSecret(value);
|
|
41
28
|
}
|
|
42
29
|
throw new Error('Requested secret not found');
|
|
43
30
|
}
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
* Checks local storage first, then falls back to environment variables.
|
|
47
|
-
* @param key - The key of the secret to retrieve.
|
|
48
|
-
* @returns A promise that resolves to the secret value.
|
|
49
|
-
* @throws {Error} If the secret is not found.
|
|
50
|
-
*/
|
|
51
|
-
async getSecret(key) {
|
|
52
|
-
// Check local storage first
|
|
53
|
-
const localValue = this.localSecrets.get(key);
|
|
54
|
-
if (localValue) {
|
|
55
|
-
return localValue;
|
|
56
|
-
}
|
|
57
|
-
// Fall back to environment variables
|
|
58
|
-
const value = await this.variables.get(key);
|
|
59
|
-
if (value) {
|
|
60
|
-
return value;
|
|
61
|
-
}
|
|
62
|
-
throw new Error('Requested secret not found');
|
|
63
|
-
}
|
|
64
|
-
/**
|
|
65
|
-
* Stores a JSON value as a secret in local storage.
|
|
66
|
-
* @param key - The key to store the secret under.
|
|
67
|
-
* @param value - The JSON value to store.
|
|
68
|
-
* @returns A promise that resolves when the secret is stored.
|
|
69
|
-
*/
|
|
70
|
-
async setSecretJSON(key, value) {
|
|
71
|
-
this.localSecrets.set(key, JSON.stringify(value));
|
|
31
|
+
async setSecret(key, value) {
|
|
32
|
+
this.localSecrets.set(key, typeof value === 'string' ? value : JSON.stringify(value));
|
|
72
33
|
}
|
|
73
|
-
/**
|
|
74
|
-
* Checks if a secret exists without throwing.
|
|
75
|
-
* @param key - The key of the secret to check.
|
|
76
|
-
* @returns A promise that resolves to true if the secret exists.
|
|
77
|
-
*/
|
|
78
34
|
async hasSecret(key) {
|
|
79
35
|
if (this.localSecrets.has(key)) {
|
|
80
36
|
return true;
|
|
@@ -82,11 +38,6 @@ export class LocalSecretService {
|
|
|
82
38
|
const value = await this.variables.get(key);
|
|
83
39
|
return value !== undefined && value !== null && value !== '';
|
|
84
40
|
}
|
|
85
|
-
/**
|
|
86
|
-
* Deletes a secret from local storage.
|
|
87
|
-
* @param key - The key of the secret to delete.
|
|
88
|
-
* @returns A promise that resolves when the secret is deleted.
|
|
89
|
-
*/
|
|
90
41
|
async deleteSecret(key) {
|
|
91
42
|
this.localSecrets.delete(key);
|
|
92
43
|
}
|
|
@@ -2,11 +2,9 @@ import type { VariablesService } from './variables-service.js';
|
|
|
2
2
|
export declare class LocalVariablesService implements VariablesService {
|
|
3
3
|
private variables;
|
|
4
4
|
constructor(variables?: Record<string, string | undefined>);
|
|
5
|
-
getAll():
|
|
6
|
-
get(name: string):
|
|
7
|
-
|
|
8
|
-
set(name: string, value: string): void;
|
|
9
|
-
setJSON(name: string, value: unknown): void;
|
|
5
|
+
getAll(): Record<string, string | undefined>;
|
|
6
|
+
get<T = string>(name: string): T | undefined;
|
|
7
|
+
set(name: string, value: unknown): void;
|
|
10
8
|
has(name: string): boolean;
|
|
11
9
|
delete(name: string): void;
|
|
12
10
|
}
|
|
@@ -7,19 +7,19 @@ export class LocalVariablesService {
|
|
|
7
7
|
return this.variables || {};
|
|
8
8
|
}
|
|
9
9
|
get(name) {
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
getJSON(name) {
|
|
13
|
-
const value = this.variables[name];
|
|
14
|
-
if (value === undefined)
|
|
10
|
+
const raw = this.variables[name];
|
|
11
|
+
if (raw === undefined)
|
|
15
12
|
return undefined;
|
|
16
|
-
|
|
13
|
+
try {
|
|
14
|
+
return JSON.parse(raw);
|
|
15
|
+
}
|
|
16
|
+
catch {
|
|
17
|
+
return raw;
|
|
18
|
+
}
|
|
17
19
|
}
|
|
18
20
|
set(name, value) {
|
|
19
|
-
this.variables[name] =
|
|
20
|
-
|
|
21
|
-
setJSON(name, value) {
|
|
22
|
-
this.variables[name] = JSON.stringify(value);
|
|
21
|
+
this.variables[name] =
|
|
22
|
+
typeof value === 'string' ? value : JSON.stringify(value);
|
|
23
23
|
}
|
|
24
24
|
has(name) {
|
|
25
25
|
return name in this.variables && this.variables[name] !== undefined;
|
|
@@ -8,9 +8,8 @@ export declare class ScopedSecretService implements SecretService {
|
|
|
8
8
|
private allowedKeys;
|
|
9
9
|
constructor(secrets: SecretService, allowedKeys: Set<string>);
|
|
10
10
|
private assertAllowed;
|
|
11
|
-
getSecret(key: string): Promise<
|
|
12
|
-
getSecretJSON<T = {}>(key: string): Promise<T>;
|
|
11
|
+
getSecret<T = string>(key: string): Promise<T>;
|
|
13
12
|
hasSecret(key: string): Promise<boolean>;
|
|
14
|
-
|
|
13
|
+
setSecret(_key: string, _value: unknown): Promise<void>;
|
|
15
14
|
deleteSecret(_key: string): Promise<void>;
|
|
16
15
|
}
|
|
@@ -18,16 +18,12 @@ export class ScopedSecretService {
|
|
|
18
18
|
this.assertAllowed(key);
|
|
19
19
|
return this.secrets.getSecret(key);
|
|
20
20
|
}
|
|
21
|
-
async getSecretJSON(key) {
|
|
22
|
-
this.assertAllowed(key);
|
|
23
|
-
return this.secrets.getSecretJSON(key);
|
|
24
|
-
}
|
|
25
21
|
async hasSecret(key) {
|
|
26
22
|
this.assertAllowed(key);
|
|
27
23
|
return this.secrets.hasSecret(key);
|
|
28
24
|
}
|
|
29
|
-
async
|
|
30
|
-
throw new Error('
|
|
25
|
+
async setSecret(_key, _value) {
|
|
26
|
+
throw new Error('setSecret is not allowed in scoped secret service');
|
|
31
27
|
}
|
|
32
28
|
async deleteSecret(_key) {
|
|
33
29
|
throw new Error('deleteSecret is not allowed in scoped secret service');
|
|
@@ -3,19 +3,12 @@
|
|
|
3
3
|
*/
|
|
4
4
|
export interface SecretService {
|
|
5
5
|
/**
|
|
6
|
-
* Retrieves a secret by key
|
|
7
|
-
* @param key - The key of the secret to retrieve.
|
|
8
|
-
* @returns A promise that resolves to the parsed secret value.
|
|
9
|
-
* @throws If the secret is not found.
|
|
10
|
-
*/
|
|
11
|
-
getSecretJSON<Return = {}>(key: string): Promise<Return>;
|
|
12
|
-
/**
|
|
13
|
-
* Retrieves a secret by key as a string.
|
|
6
|
+
* Retrieves a secret by key, typed as T (defaults to string).
|
|
14
7
|
* @param key - The key of the secret to retrieve.
|
|
15
8
|
* @returns A promise that resolves to the secret value.
|
|
16
9
|
* @throws If the secret is not found.
|
|
17
10
|
*/
|
|
18
|
-
getSecret(key: string): Promise<
|
|
11
|
+
getSecret<T = string>(key: string): Promise<T>;
|
|
19
12
|
/**
|
|
20
13
|
* Checks if a secret exists without throwing.
|
|
21
14
|
* @param key - The key of the secret to check.
|
|
@@ -23,12 +16,12 @@ export interface SecretService {
|
|
|
23
16
|
*/
|
|
24
17
|
hasSecret(key: string): Promise<boolean>;
|
|
25
18
|
/**
|
|
26
|
-
* Stores a
|
|
19
|
+
* Stores a secret value.
|
|
27
20
|
* @param key - The key to store the secret under.
|
|
28
|
-
* @param value - The
|
|
21
|
+
* @param value - The value to store.
|
|
29
22
|
* @returns A promise that resolves when the secret is stored.
|
|
30
23
|
*/
|
|
31
|
-
|
|
24
|
+
setSecret(key: string, value: unknown): Promise<void>;
|
|
32
25
|
/**
|
|
33
26
|
* Deletes a secret by key.
|
|
34
27
|
* @param key - The key of the secret to delete.
|
|
@@ -19,11 +19,10 @@ export declare class TypedSecretService<TMap = Record<string, unknown>> implemen
|
|
|
19
19
|
private secrets;
|
|
20
20
|
private credentialsMeta;
|
|
21
21
|
constructor(secrets: SecretService, credentialsMeta: Record<string, CredentialMeta>);
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
getSecret(key: string): Promise<string>;
|
|
22
|
+
getSecret<K extends keyof TMap & string>(key: K): Promise<TMap[K]>;
|
|
23
|
+
getSecret<T = string>(key: string): Promise<T>;
|
|
25
24
|
hasSecret(key: string): Promise<boolean>;
|
|
26
|
-
|
|
25
|
+
setSecret<K extends string>(key: K, value: K extends keyof TMap ? TMap[K] : unknown): Promise<void>;
|
|
27
26
|
deleteSecret(key: string): Promise<void>;
|
|
28
27
|
getAllStatus(): Promise<CredentialStatus[]>;
|
|
29
28
|
getMissing(): Promise<CredentialStatus[]>;
|
|
@@ -5,17 +5,14 @@ export class TypedSecretService {
|
|
|
5
5
|
this.secrets = secrets;
|
|
6
6
|
this.credentialsMeta = credentialsMeta;
|
|
7
7
|
}
|
|
8
|
-
async getSecretJSON(key) {
|
|
9
|
-
return this.secrets.getSecretJSON(key);
|
|
10
|
-
}
|
|
11
8
|
async getSecret(key) {
|
|
12
9
|
return this.secrets.getSecret(key);
|
|
13
10
|
}
|
|
14
11
|
async hasSecret(key) {
|
|
15
12
|
return this.secrets.hasSecret(key);
|
|
16
13
|
}
|
|
17
|
-
async
|
|
18
|
-
return this.secrets.
|
|
14
|
+
async setSecret(key, value) {
|
|
15
|
+
return this.secrets.setSecret(key, value);
|
|
19
16
|
}
|
|
20
17
|
async deleteSecret(key) {
|
|
21
18
|
return this.secrets.deleteSecret(key);
|
|
@@ -13,13 +13,10 @@ export declare class TypedVariablesService<TMap = Record<string, unknown>> imple
|
|
|
13
13
|
private variables;
|
|
14
14
|
private variablesMeta;
|
|
15
15
|
constructor(variables: VariablesService, variablesMeta: Record<string, VariableMeta>);
|
|
16
|
-
get
|
|
17
|
-
get(name: string): Promise<
|
|
18
|
-
getJSON<K extends keyof TMap & string>(name: K): Promise<TMap[K] | undefined> | TMap[K] | undefined;
|
|
19
|
-
getJSON<T = unknown>(name: string): Promise<T | undefined> | T | undefined;
|
|
16
|
+
get<K extends keyof TMap & string>(name: K): Promise<TMap[K] | undefined> | TMap[K] | undefined;
|
|
17
|
+
get<T = string>(name: string): Promise<T | undefined> | T | undefined;
|
|
20
18
|
getAll(): Promise<Record<string, string | undefined>> | Record<string, string | undefined>;
|
|
21
|
-
set(name: string, value:
|
|
22
|
-
setJSON(name: string, value: unknown): Promise<void> | void;
|
|
19
|
+
set(name: string, value: unknown): Promise<void> | void;
|
|
23
20
|
has(name: string): Promise<boolean> | boolean;
|
|
24
21
|
delete(name: string): Promise<void> | void;
|
|
25
22
|
getAllStatus(): Promise<VariableStatus[]>;
|
|
@@ -8,18 +8,12 @@ export class TypedVariablesService {
|
|
|
8
8
|
get(name) {
|
|
9
9
|
return this.variables.get(name);
|
|
10
10
|
}
|
|
11
|
-
getJSON(name) {
|
|
12
|
-
return this.variables.getJSON(name);
|
|
13
|
-
}
|
|
14
11
|
getAll() {
|
|
15
12
|
return this.variables.getAll();
|
|
16
13
|
}
|
|
17
14
|
set(name, value) {
|
|
18
15
|
return this.variables.set(name, value);
|
|
19
16
|
}
|
|
20
|
-
setJSON(name, value) {
|
|
21
|
-
return this.variables.setJSON(name, value);
|
|
22
|
-
}
|
|
23
17
|
has(name) {
|
|
24
18
|
return this.variables.has(name);
|
|
25
19
|
}
|
|
@@ -1,9 +1,7 @@
|
|
|
1
1
|
export interface VariablesService {
|
|
2
|
-
get
|
|
3
|
-
getJSON: <T = unknown>(name: string) => Promise<T | undefined> | T | undefined;
|
|
2
|
+
get<T = string>(name: string): Promise<T | undefined> | T | undefined;
|
|
4
3
|
getAll: () => Promise<Record<string, string | undefined>> | Record<string, string | undefined>;
|
|
5
|
-
set: (name: string, value:
|
|
6
|
-
setJSON: (name: string, value: unknown) => Promise<void> | void;
|
|
4
|
+
set: (name: string, value: unknown) => Promise<void> | void;
|
|
7
5
|
has: (name: string) => Promise<boolean> | boolean;
|
|
8
6
|
delete: (name: string) => Promise<void> | void;
|
|
9
7
|
}
|
|
@@ -517,13 +517,13 @@ export function defineServiceTests(config) {
|
|
|
517
517
|
const factory = services.secretService;
|
|
518
518
|
const kek = 'test-key-encryption-key-32chars!';
|
|
519
519
|
describe(`SecretService [${name}]`, () => {
|
|
520
|
-
test('
|
|
520
|
+
test('setSecret and getSecret', async () => {
|
|
521
521
|
const service = await factory({ key: kek });
|
|
522
|
-
await service.
|
|
522
|
+
await service.setSecret('api-key', {
|
|
523
523
|
token: 'sk-123',
|
|
524
524
|
endpoint: 'https://api.example.com',
|
|
525
525
|
});
|
|
526
|
-
const result = await service.
|
|
526
|
+
const result = await service.getSecret('api-key');
|
|
527
527
|
assert.deepEqual(result, {
|
|
528
528
|
token: 'sk-123',
|
|
529
529
|
endpoint: 'https://api.example.com',
|
|
@@ -531,9 +531,9 @@ export function defineServiceTests(config) {
|
|
|
531
531
|
});
|
|
532
532
|
test('getSecret returns raw string', async () => {
|
|
533
533
|
const service = await factory({ key: kek });
|
|
534
|
-
await service.
|
|
534
|
+
await service.setSecret('string-secret', 'plain-value');
|
|
535
535
|
const result = await service.getSecret('string-secret');
|
|
536
|
-
assert.strictEqual(result, '
|
|
536
|
+
assert.strictEqual(result, 'plain-value');
|
|
537
537
|
});
|
|
538
538
|
test('hasSecret returns true/false', async () => {
|
|
539
539
|
const service = await factory({ key: kek });
|
|
@@ -546,16 +546,16 @@ export function defineServiceTests(config) {
|
|
|
546
546
|
message: 'Requested secret not found',
|
|
547
547
|
});
|
|
548
548
|
});
|
|
549
|
-
test('
|
|
549
|
+
test('setSecret upserts existing key', async () => {
|
|
550
550
|
const service = await factory({ key: kek });
|
|
551
|
-
await service.
|
|
552
|
-
await service.
|
|
553
|
-
const result = await service.
|
|
551
|
+
await service.setSecret('upsert-key', { v: 1 });
|
|
552
|
+
await service.setSecret('upsert-key', { v: 2 });
|
|
553
|
+
const result = await service.getSecret('upsert-key');
|
|
554
554
|
assert.deepEqual(result, { v: 2 });
|
|
555
555
|
});
|
|
556
556
|
test('deleteSecret removes the key', async () => {
|
|
557
557
|
const service = await factory({ key: kek });
|
|
558
|
-
await service.
|
|
558
|
+
await service.setSecret('to-delete', 'bye');
|
|
559
559
|
assert.strictEqual(await service.hasSecret('to-delete'), true);
|
|
560
560
|
await service.deleteSecret('to-delete');
|
|
561
561
|
assert.strictEqual(await service.hasSecret('to-delete'), false);
|
|
@@ -563,13 +563,13 @@ export function defineServiceTests(config) {
|
|
|
563
563
|
test('rotateKEK re-wraps all secrets', async () => {
|
|
564
564
|
const newKEK = 'new-key-encryption-key-rotated!';
|
|
565
565
|
const oldService = await factory({ key: kek });
|
|
566
|
-
await oldService.
|
|
566
|
+
await oldService.setSecret('rotate-test', { important: 'data' });
|
|
567
567
|
const rotatedService = await factory({
|
|
568
568
|
key: newKEK,
|
|
569
569
|
keyVersion: 2,
|
|
570
570
|
previousKey: kek,
|
|
571
571
|
});
|
|
572
|
-
const before = await rotatedService.
|
|
572
|
+
const before = await rotatedService.getSecret('rotate-test');
|
|
573
573
|
assert.deepEqual(before, { important: 'data' });
|
|
574
574
|
assert.ok(rotatedService.rotateKEK);
|
|
575
575
|
const count = await rotatedService.rotateKEK();
|
|
@@ -578,7 +578,7 @@ export function defineServiceTests(config) {
|
|
|
578
578
|
key: newKEK,
|
|
579
579
|
keyVersion: 2,
|
|
580
580
|
});
|
|
581
|
-
const after = await newOnlyService.
|
|
581
|
+
const after = await newOnlyService.getSecret('rotate-test');
|
|
582
582
|
assert.deepEqual(after, { important: 'data' });
|
|
583
583
|
});
|
|
584
584
|
test('rotateKEK throws without previousKey', async () => {
|
|
@@ -494,7 +494,10 @@ export async function prepareAgentRun(agentName, input, params, agentSessionMap,
|
|
|
494
494
|
const trimmedMessages = trimMessages(allMessages);
|
|
495
495
|
const aiMiddlewares = agent.aiMiddleware ?? [];
|
|
496
496
|
const { tools, missingRpcs } = await buildToolDefs(params, agentSessionMap, input.resourceId, resolvedName, packageName, streamContext, aiMiddlewares, agent.agentMode);
|
|
497
|
-
|
|
497
|
+
let instructions = await buildInstructions(resolvedName, packageName);
|
|
498
|
+
if (input.context) {
|
|
499
|
+
instructions = `${instructions}\n\nCurrent context (use these identifiers directly in tool calls — do not ask the user for them):\n${input.context}`;
|
|
500
|
+
}
|
|
498
501
|
const resolved = resolveModelConfig(resolvedName, agent);
|
|
499
502
|
// Per-request overrides
|
|
500
503
|
if (input.model) {
|
|
@@ -76,6 +76,10 @@ export interface AIAgentInput {
|
|
|
76
76
|
attachments?: AIAgentInputAttachment[];
|
|
77
77
|
model?: string;
|
|
78
78
|
temperature?: number;
|
|
79
|
+
/** Structured context injected into the system instructions for this request.
|
|
80
|
+
* Use to provide upfront state (e.g. current org/project/branch/deployment)
|
|
81
|
+
* so the agent can call tools without asking the user for identifiers. */
|
|
82
|
+
context?: string;
|
|
79
83
|
}
|
|
80
84
|
export interface AIAgentOutput {
|
|
81
85
|
runId: string;
|