@pikku/mongodb 0.12.4 → 0.12.6
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 +27 -1
- package/dist/src/mongodb-agent-run-service.d.ts +1 -0
- package/dist/src/mongodb-agent-run-service.js +4 -1
- package/dist/src/mongodb-channel-store.d.ts +3 -0
- package/dist/src/mongodb-channel-store.js +10 -0
- package/dist/src/mongodb-secret-service.d.ts +3 -3
- package/dist/src/mongodb-secret-service.js +18 -7
- package/dist/src/mongodb-workflow-run-service.js +2 -0
- package/dist/src/mongodb-workflow-service.d.ts +17 -14
- package/dist/src/mongodb-workflow-service.js +15 -13
- package/package.json +5 -2
- package/src/mongodb-agent-run-service.ts +6 -1
- package/src/mongodb-channel-store.ts +17 -0
- package/src/mongodb-secret-service.ts +24 -17
- package/src/mongodb-services.test.ts +2 -2
- package/src/mongodb-workflow-run-service.ts +5 -0
- package/src/mongodb-workflow-service.ts +35 -14
package/CHANGELOG.md
CHANGED
|
@@ -1,11 +1,37 @@
|
|
|
1
1
|
# @pikku/mongodb
|
|
2
2
|
|
|
3
|
+
## 0.12.6
|
|
4
|
+
|
|
5
|
+
### Patch Changes
|
|
6
|
+
|
|
7
|
+
- 4b5c75b: feat(auth-js): wire OIDC config (issuer/tenantId) as variables, expand provider registry
|
|
8
|
+
- Move `issuer` and `tenantId` out of the secret blob for OIDC providers (auth0, okta, azure-ad, keycloak, cognito, microsoft-entra-id) — they are public config URLs, not secrets. Now registered via `wireVariable` and loaded at runtime via `services.variables.get()`.
|
|
9
|
+
- Expand provider registry from 13 to 31 providers: reddit, notion, instagram, zoom, figma, tiktok, threads, patreon, dropbox, bitbucket, hubspot, salesforce, atlassian, strava, keycloak, cognito, microsoft-entra-id added.
|
|
10
|
+
- `serialize-auth-gen` emits `wireVariable({...})` declarations and `services.variables.get()` calls in the generated factory for OIDC providers.
|
|
11
|
+
- Integration verifier exercises real `/auth/providers` endpoint with `LocalSecretService` + `LocalVariablesService`, including a spy test proving `services.variables.get('AUTH0_ISSUER')` is called at request time.
|
|
12
|
+
|
|
13
|
+
- Updated dependencies [4b5c75b]
|
|
14
|
+
- Updated dependencies [4b5c75b]
|
|
15
|
+
- @pikku/core@0.12.27
|
|
16
|
+
|
|
17
|
+
## 0.12.5
|
|
18
|
+
|
|
19
|
+
### Patch Changes
|
|
20
|
+
|
|
21
|
+
- b9ed73e: Add deterministic workflow planned-step metadata support and SSE init stream payload generation.
|
|
22
|
+
- Persist `deterministic` and `plannedSteps` on workflow runs in core and service adapters.
|
|
23
|
+
- Expose planned-step metadata on workflow run status responses.
|
|
24
|
+
- Emit an initial `type: 'init'` SSE event for deterministic workflow streams before incremental updates.
|
|
25
|
+
- Add CLI tests covering serialized stream route output for init/update/done event behavior.
|
|
26
|
+
|
|
27
|
+
- Updated dependencies [b9ed73e]
|
|
28
|
+
- @pikku/core@0.12.19
|
|
29
|
+
|
|
3
30
|
## 0.12.4
|
|
4
31
|
|
|
5
32
|
### Patch Changes
|
|
6
33
|
|
|
7
34
|
- 311c0c4: Unify session persistence through SessionStore, remove session blob from ChannelStore
|
|
8
|
-
|
|
9
35
|
- PikkuSessionService now persists sessions via SessionStore on set()/clear() instead of every function call
|
|
10
36
|
- ChannelStore no longer stores session data — maps channelId to pikkuUserId only
|
|
11
37
|
- ChannelStore API: setUserSession/getChannelAndSession replaced with setPikkuUserId/getChannel
|
|
@@ -10,8 +10,11 @@ export class MongoDBAgentRunService {
|
|
|
10
10
|
this.runs = db.collection('ai_run');
|
|
11
11
|
}
|
|
12
12
|
async listThreads(options) {
|
|
13
|
-
const { agentName, limit = 50, offset = 0 } = options ?? {};
|
|
13
|
+
const { agentName, resourceId, limit = 50, offset = 0 } = options ?? {};
|
|
14
14
|
let filter = {};
|
|
15
|
+
if (resourceId) {
|
|
16
|
+
filter.resourceId = resourceId;
|
|
17
|
+
}
|
|
15
18
|
if (agentName) {
|
|
16
19
|
const threadIds = await this.runs.distinct('threadId', {
|
|
17
20
|
agentName,
|
|
@@ -14,5 +14,8 @@ export declare class MongoDBChannelStore extends ChannelStore {
|
|
|
14
14
|
getChannel(channelId: string): Promise<Channel & {
|
|
15
15
|
pikkuUserId?: string;
|
|
16
16
|
}>;
|
|
17
|
+
setState(channelId: string, state: unknown): Promise<void>;
|
|
18
|
+
getState(channelId: string): Promise<unknown | undefined>;
|
|
19
|
+
clearState(channelId: string): Promise<void>;
|
|
17
20
|
close(): Promise<void>;
|
|
18
21
|
}
|
|
@@ -48,5 +48,15 @@ export class MongoDBChannelStore extends ChannelStore {
|
|
|
48
48
|
pikkuUserId: row.pikkuUserId ?? undefined,
|
|
49
49
|
};
|
|
50
50
|
}
|
|
51
|
+
async setState(channelId, state) {
|
|
52
|
+
await this.channels.updateOne({ _id: channelId }, { $set: { state } });
|
|
53
|
+
}
|
|
54
|
+
async getState(channelId) {
|
|
55
|
+
const row = await this.channels.findOne({ _id: channelId }, { projection: { state: 1 } });
|
|
56
|
+
return row?.state ?? undefined;
|
|
57
|
+
}
|
|
58
|
+
async clearState(channelId) {
|
|
59
|
+
await this.channels.updateOne({ _id: channelId }, { $unset: { state: '' } });
|
|
60
|
+
}
|
|
51
61
|
async close() { }
|
|
52
62
|
}
|
|
@@ -21,10 +21,10 @@ export declare class MongoDBSecretService implements SecretService {
|
|
|
21
21
|
init(): Promise<void>;
|
|
22
22
|
private logAudit;
|
|
23
23
|
private getKEK;
|
|
24
|
-
getSecret(key: string): Promise<
|
|
25
|
-
getSecretJSON<R = {}>(key: string): Promise<R>;
|
|
24
|
+
getSecret<T = string>(key: string): Promise<T>;
|
|
26
25
|
hasSecret(key: string): Promise<boolean>;
|
|
27
|
-
|
|
26
|
+
setSecret(key: string, value: unknown): Promise<void>;
|
|
28
27
|
deleteSecret(key: string): Promise<void>;
|
|
28
|
+
getSecrets(keys: string[]): Promise<Record<string, unknown>>;
|
|
29
29
|
rotateKEK(): Promise<number>;
|
|
30
30
|
}
|
|
@@ -56,17 +56,12 @@ export class MongoDBSecretService {
|
|
|
56
56
|
await this.logAudit(key, 'read');
|
|
57
57
|
return result;
|
|
58
58
|
}
|
|
59
|
-
async getSecretJSON(key) {
|
|
60
|
-
const raw = await this.getSecret(key);
|
|
61
|
-
return JSON.parse(raw);
|
|
62
|
-
}
|
|
63
59
|
async hasSecret(key) {
|
|
64
60
|
const count = await this.secrets.countDocuments({ _id: key }, { limit: 1 });
|
|
65
61
|
return count > 0;
|
|
66
62
|
}
|
|
67
|
-
async
|
|
68
|
-
const
|
|
69
|
-
const { ciphertext, wrappedDEK } = await envelopeEncrypt(this.key, plaintext);
|
|
63
|
+
async setSecret(key, value) {
|
|
64
|
+
const { ciphertext, wrappedDEK } = await envelopeEncrypt(this.key, value);
|
|
70
65
|
const now = new Date();
|
|
71
66
|
await this.secrets.updateOne({ _id: key }, {
|
|
72
67
|
$set: {
|
|
@@ -86,6 +81,22 @@ export class MongoDBSecretService {
|
|
|
86
81
|
await this.secrets.deleteOne({ _id: key });
|
|
87
82
|
await this.logAudit(key, 'delete');
|
|
88
83
|
}
|
|
84
|
+
async getSecrets(keys) {
|
|
85
|
+
const rows = await this.secrets
|
|
86
|
+
.find({ _id: { $in: keys } })
|
|
87
|
+
.toArray();
|
|
88
|
+
const out = {};
|
|
89
|
+
for (const row of rows) {
|
|
90
|
+
try {
|
|
91
|
+
const kek = this.getKEK(row.keyVersion);
|
|
92
|
+
out[row._id] = await envelopeDecrypt(kek, row.ciphertext, row.wrappedDek);
|
|
93
|
+
}
|
|
94
|
+
catch {
|
|
95
|
+
// skip secrets that fail to decrypt
|
|
96
|
+
}
|
|
97
|
+
}
|
|
98
|
+
return out;
|
|
99
|
+
}
|
|
89
100
|
async rotateKEK() {
|
|
90
101
|
if (!this.previousKey) {
|
|
91
102
|
throw new Error('No previousKey configured — nothing to rotate from');
|
|
@@ -138,6 +138,8 @@ export class MongoDBWorkflowRunService {
|
|
|
138
138
|
error: row.error ?? undefined,
|
|
139
139
|
inline: row.inline,
|
|
140
140
|
graphHash: row.graphHash ?? undefined,
|
|
141
|
+
deterministic: row.deterministic ?? undefined,
|
|
142
|
+
plannedSteps: row.plannedSteps ?? undefined,
|
|
141
143
|
wire: row.wire ?? { type: 'unknown' },
|
|
142
144
|
createdAt: new Date(row.createdAt),
|
|
143
145
|
updatedAt: new Date(row.updatedAt),
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import type { SerializedError } from '@pikku/core';
|
|
2
|
-
import { PikkuWorkflowService, type WorkflowRun, type WorkflowRunWire, type StepState, type WorkflowStatus, type WorkflowVersionStatus } from '@pikku/core/workflow';
|
|
2
|
+
import { PikkuWorkflowService, type WorkflowPlannedStep, type WorkflowRun, type WorkflowRunWire, type StepState, type WorkflowStatus, type WorkflowVersionStatus } from '@pikku/core/workflow';
|
|
3
3
|
import type { Db } from 'mongodb';
|
|
4
4
|
export declare class MongoDBWorkflowService extends PikkuWorkflowService {
|
|
5
5
|
private initialized;
|
|
@@ -10,10 +10,13 @@ export declare class MongoDBWorkflowService extends PikkuWorkflowService {
|
|
|
10
10
|
private versions;
|
|
11
11
|
constructor(db: Db);
|
|
12
12
|
init(): Promise<void>;
|
|
13
|
-
|
|
13
|
+
protected createRunImpl(workflowName: string, input: any, inline: boolean, graphHash: string, wire: WorkflowRunWire, options?: {
|
|
14
|
+
deterministic?: boolean;
|
|
15
|
+
plannedSteps?: WorkflowPlannedStep[];
|
|
16
|
+
}): Promise<string>;
|
|
14
17
|
getRun(id: string): Promise<WorkflowRun | null>;
|
|
15
|
-
|
|
16
|
-
|
|
18
|
+
protected updateRunStatusImpl(id: string, status: WorkflowStatus, output?: any, error?: SerializedError): Promise<void>;
|
|
19
|
+
protected insertStepStateImpl(runId: string, stepName: string, rpcName: string | null, data: any, stepOptions?: {
|
|
17
20
|
retries?: number;
|
|
18
21
|
retryDelay?: string | number;
|
|
19
22
|
}): Promise<StepState>;
|
|
@@ -21,14 +24,14 @@ export declare class MongoDBWorkflowService extends PikkuWorkflowService {
|
|
|
21
24
|
getRunHistory(runId: string): Promise<Array<StepState & {
|
|
22
25
|
stepName: string;
|
|
23
26
|
}>>;
|
|
24
|
-
|
|
25
|
-
|
|
27
|
+
protected setStepRunningImpl(stepId: string): Promise<void>;
|
|
28
|
+
protected setStepScheduledImpl(stepId: string): Promise<void>;
|
|
26
29
|
private insertHistoryRecord;
|
|
27
30
|
private getTimestampFieldForStatus;
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
31
|
+
protected setStepChildRunIdImpl(stepId: string, childRunId: string): Promise<void>;
|
|
32
|
+
protected setStepResultImpl(stepId: string, result: any): Promise<void>;
|
|
33
|
+
protected setStepErrorImpl(stepId: string, error: Error): Promise<void>;
|
|
34
|
+
protected createRetryAttemptImpl(stepId: string, status: 'pending' | 'running'): Promise<StepState>;
|
|
32
35
|
withRunLock<T>(id: string, fn: () => Promise<T>): Promise<T>;
|
|
33
36
|
withStepLock<T>(_runId: string, _stepName: string, fn: () => Promise<T>): Promise<T>;
|
|
34
37
|
getCompletedGraphState(runId: string): Promise<{
|
|
@@ -38,11 +41,11 @@ export declare class MongoDBWorkflowService extends PikkuWorkflowService {
|
|
|
38
41
|
}>;
|
|
39
42
|
getNodesWithoutSteps(runId: string, nodeIds: string[]): Promise<string[]>;
|
|
40
43
|
getNodeResults(runId: string, nodeIds: string[]): Promise<Record<string, any>>;
|
|
41
|
-
|
|
42
|
-
|
|
44
|
+
protected setBranchTakenImpl(stepId: string, branchKey: string): Promise<void>;
|
|
45
|
+
protected updateRunStateImpl(runId: string, name: string, value: unknown): Promise<void>;
|
|
43
46
|
getRunState(runId: string): Promise<Record<string, unknown>>;
|
|
44
|
-
|
|
45
|
-
|
|
47
|
+
protected upsertWorkflowVersionImpl(name: string, graphHash: string, graph: any, source: string, status?: WorkflowVersionStatus): Promise<void>;
|
|
48
|
+
protected updateWorkflowVersionStatusImpl(name: string, graphHash: string, status: WorkflowVersionStatus): Promise<void>;
|
|
46
49
|
getWorkflowVersion(name: string, graphHash: string): Promise<{
|
|
47
50
|
graph: any;
|
|
48
51
|
source: string;
|
|
@@ -28,7 +28,7 @@ export class MongoDBWorkflowService extends PikkuWorkflowService {
|
|
|
28
28
|
await this.versions.createIndex({ workflowName: 1, graphHash: 1 }, { unique: true });
|
|
29
29
|
this.initialized = true;
|
|
30
30
|
}
|
|
31
|
-
async
|
|
31
|
+
async createRunImpl(workflowName, input, inline, graphHash, wire, options) {
|
|
32
32
|
const id = crypto.randomUUID();
|
|
33
33
|
const now = new Date();
|
|
34
34
|
await this.runs.insertOne({
|
|
@@ -41,6 +41,8 @@ export class MongoDBWorkflowService extends PikkuWorkflowService {
|
|
|
41
41
|
state: {},
|
|
42
42
|
inline,
|
|
43
43
|
graphHash,
|
|
44
|
+
deterministic: options?.deterministic ?? false,
|
|
45
|
+
plannedSteps: options?.plannedSteps ?? [],
|
|
44
46
|
wire,
|
|
45
47
|
createdAt: now,
|
|
46
48
|
updatedAt: now,
|
|
@@ -50,7 +52,7 @@ export class MongoDBWorkflowService extends PikkuWorkflowService {
|
|
|
50
52
|
async getRun(id) {
|
|
51
53
|
return this.runService.getRun(id);
|
|
52
54
|
}
|
|
53
|
-
async
|
|
55
|
+
async updateRunStatusImpl(id, status, output, error) {
|
|
54
56
|
await this.runs.updateOne({ _id: id }, {
|
|
55
57
|
$set: {
|
|
56
58
|
status,
|
|
@@ -60,7 +62,7 @@ export class MongoDBWorkflowService extends PikkuWorkflowService {
|
|
|
60
62
|
},
|
|
61
63
|
});
|
|
62
64
|
}
|
|
63
|
-
async
|
|
65
|
+
async insertStepStateImpl(runId, stepName, rpcName, data, stepOptions) {
|
|
64
66
|
const stepId = crypto.randomUUID();
|
|
65
67
|
const now = new Date();
|
|
66
68
|
await this.steps.insertOne({
|
|
@@ -117,7 +119,7 @@ export class MongoDBWorkflowService extends PikkuWorkflowService {
|
|
|
117
119
|
async getRunHistory(runId) {
|
|
118
120
|
return this.runService.getRunHistory(runId);
|
|
119
121
|
}
|
|
120
|
-
async
|
|
122
|
+
async setStepRunningImpl(stepId) {
|
|
121
123
|
await this.steps.updateOne({ _id: stepId }, { $set: { status: 'running', updatedAt: new Date() } });
|
|
122
124
|
const latestHistory = await this.stepHistory
|
|
123
125
|
.find({ workflowStepId: stepId })
|
|
@@ -128,7 +130,7 @@ export class MongoDBWorkflowService extends PikkuWorkflowService {
|
|
|
128
130
|
await this.stepHistory.updateOne({ _id: latestHistory[0]._id }, { $set: { status: 'running' } });
|
|
129
131
|
}
|
|
130
132
|
}
|
|
131
|
-
async
|
|
133
|
+
async setStepScheduledImpl(stepId) {
|
|
132
134
|
await this.steps.updateOne({ _id: stepId }, { $set: { status: 'scheduled', updatedAt: new Date() } });
|
|
133
135
|
}
|
|
134
136
|
async insertHistoryRecord(stepId, status, result, error) {
|
|
@@ -165,7 +167,7 @@ export class MongoDBWorkflowService extends PikkuWorkflowService {
|
|
|
165
167
|
return 'createdAt';
|
|
166
168
|
}
|
|
167
169
|
}
|
|
168
|
-
async
|
|
170
|
+
async setStepChildRunIdImpl(stepId, childRunId) {
|
|
169
171
|
await this.steps.updateOne({ _id: stepId }, {
|
|
170
172
|
$set: {
|
|
171
173
|
childRunId,
|
|
@@ -173,7 +175,7 @@ export class MongoDBWorkflowService extends PikkuWorkflowService {
|
|
|
173
175
|
},
|
|
174
176
|
});
|
|
175
177
|
}
|
|
176
|
-
async
|
|
178
|
+
async setStepResultImpl(stepId, result) {
|
|
177
179
|
await this.steps.updateOne({ _id: stepId }, {
|
|
178
180
|
$set: {
|
|
179
181
|
status: 'succeeded',
|
|
@@ -191,7 +193,7 @@ export class MongoDBWorkflowService extends PikkuWorkflowService {
|
|
|
191
193
|
await this.stepHistory.updateOne({ _id: latestHistory[0]._id }, { $set: { status: 'succeeded', result } });
|
|
192
194
|
}
|
|
193
195
|
}
|
|
194
|
-
async
|
|
196
|
+
async setStepErrorImpl(stepId, error) {
|
|
195
197
|
const serializedError = {
|
|
196
198
|
message: error.message,
|
|
197
199
|
stack: error.stack,
|
|
@@ -214,7 +216,7 @@ export class MongoDBWorkflowService extends PikkuWorkflowService {
|
|
|
214
216
|
await this.stepHistory.updateOne({ _id: latestHistory[0]._id }, { $set: { status: 'failed', error: serializedError } });
|
|
215
217
|
}
|
|
216
218
|
}
|
|
217
|
-
async
|
|
219
|
+
async createRetryAttemptImpl(stepId, status) {
|
|
218
220
|
await this.steps.updateOne({ _id: stepId }, {
|
|
219
221
|
$set: {
|
|
220
222
|
status,
|
|
@@ -307,10 +309,10 @@ export class MongoDBWorkflowService extends PikkuWorkflowService {
|
|
|
307
309
|
}
|
|
308
310
|
return results;
|
|
309
311
|
}
|
|
310
|
-
async
|
|
312
|
+
async setBranchTakenImpl(stepId, branchKey) {
|
|
311
313
|
await this.steps.updateOne({ _id: stepId }, { $set: { branchTaken: branchKey, updatedAt: new Date() } });
|
|
312
314
|
}
|
|
313
|
-
async
|
|
315
|
+
async updateRunStateImpl(runId, name, value) {
|
|
314
316
|
if (!/^[a-zA-Z0-9_]+$/.test(name)) {
|
|
315
317
|
throw new Error('Invalid state key name');
|
|
316
318
|
}
|
|
@@ -327,7 +329,7 @@ export class MongoDBWorkflowService extends PikkuWorkflowService {
|
|
|
327
329
|
return {};
|
|
328
330
|
return row.state ?? {};
|
|
329
331
|
}
|
|
330
|
-
async
|
|
332
|
+
async upsertWorkflowVersionImpl(name, graphHash, graph, source, status) {
|
|
331
333
|
await this.versions.updateOne({ workflowName: name, graphHash }, {
|
|
332
334
|
$setOnInsert: {
|
|
333
335
|
workflowName: name,
|
|
@@ -339,7 +341,7 @@ export class MongoDBWorkflowService extends PikkuWorkflowService {
|
|
|
339
341
|
},
|
|
340
342
|
}, { upsert: true });
|
|
341
343
|
}
|
|
342
|
-
async
|
|
344
|
+
async updateWorkflowVersionStatusImpl(name, graphHash, status) {
|
|
343
345
|
await this.versions.updateOne({ workflowName: name, graphHash }, { $set: { status } });
|
|
344
346
|
}
|
|
345
347
|
async getWorkflowVersion(name, graphHash) {
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@pikku/mongodb",
|
|
3
|
-
"version": "0.12.
|
|
3
|
+
"version": "0.12.6",
|
|
4
4
|
"author": "yasser.fadl@gmail.com",
|
|
5
5
|
"license": "MIT",
|
|
6
6
|
"module": "dist/src/index.js",
|
|
@@ -17,7 +17,7 @@
|
|
|
17
17
|
"prepublishOnly": "yarn build"
|
|
18
18
|
},
|
|
19
19
|
"peerDependencies": {
|
|
20
|
-
"@pikku/core": "^0.12.
|
|
20
|
+
"@pikku/core": "^0.12.27"
|
|
21
21
|
},
|
|
22
22
|
"dependencies": {
|
|
23
23
|
"mongodb": "^6.12.0"
|
|
@@ -26,5 +26,8 @@
|
|
|
26
26
|
"mongodb": "^6.12.0",
|
|
27
27
|
"mongodb-memory-server": "^10.4.0",
|
|
28
28
|
"typescript": "^5.9"
|
|
29
|
+
},
|
|
30
|
+
"exports": {
|
|
31
|
+
".": "./dist/src/index.js"
|
|
29
32
|
}
|
|
30
33
|
}
|
|
@@ -70,13 +70,18 @@ export class MongoDBAgentRunService implements AgentRunService {
|
|
|
70
70
|
|
|
71
71
|
async listThreads(options?: {
|
|
72
72
|
agentName?: string
|
|
73
|
+
resourceId?: string
|
|
73
74
|
limit?: number
|
|
74
75
|
offset?: number
|
|
75
76
|
}): Promise<AIThread[]> {
|
|
76
|
-
const { agentName, limit = 50, offset = 0 } = options ?? {}
|
|
77
|
+
const { agentName, resourceId, limit = 50, offset = 0 } = options ?? {}
|
|
77
78
|
|
|
78
79
|
let filter: Record<string, any> = {}
|
|
79
80
|
|
|
81
|
+
if (resourceId) {
|
|
82
|
+
filter.resourceId = resourceId
|
|
83
|
+
}
|
|
84
|
+
|
|
80
85
|
if (agentName) {
|
|
81
86
|
const threadIds = await this.runs.distinct('threadId', {
|
|
82
87
|
agentName,
|
|
@@ -8,6 +8,7 @@ interface ChannelDoc {
|
|
|
8
8
|
createdAt: Date
|
|
9
9
|
openingData: any
|
|
10
10
|
pikkuUserId: string | null
|
|
11
|
+
state?: unknown
|
|
11
12
|
lastWire: Date
|
|
12
13
|
}
|
|
13
14
|
|
|
@@ -88,5 +89,21 @@ export class MongoDBChannelStore extends ChannelStore {
|
|
|
88
89
|
}
|
|
89
90
|
}
|
|
90
91
|
|
|
92
|
+
public async setState(channelId: string, state: unknown): Promise<void> {
|
|
93
|
+
await this.channels.updateOne({ _id: channelId }, { $set: { state } })
|
|
94
|
+
}
|
|
95
|
+
|
|
96
|
+
public async getState(channelId: string): Promise<unknown | undefined> {
|
|
97
|
+
const row = await this.channels.findOne(
|
|
98
|
+
{ _id: channelId },
|
|
99
|
+
{ projection: { state: 1 } }
|
|
100
|
+
)
|
|
101
|
+
return row?.state ?? undefined
|
|
102
|
+
}
|
|
103
|
+
|
|
104
|
+
public async clearState(channelId: string): Promise<void> {
|
|
105
|
+
await this.channels.updateOne({ _id: channelId }, { $unset: { state: '' } })
|
|
106
|
+
}
|
|
107
|
+
|
|
91
108
|
public async close(): Promise<void> {}
|
|
92
109
|
}
|
|
@@ -87,36 +87,23 @@ export class MongoDBSecretService implements SecretService {
|
|
|
87
87
|
throw new Error(`No KEK available for key_version ${version}`)
|
|
88
88
|
}
|
|
89
89
|
|
|
90
|
-
async getSecret(key: string): Promise<
|
|
90
|
+
async getSecret<T = string>(key: string): Promise<T> {
|
|
91
91
|
const row = await this.secrets.findOne({ _id: key })
|
|
92
92
|
if (!row) throw new Error('Requested secret not found')
|
|
93
93
|
|
|
94
94
|
const kek = this.getKEK(row.keyVersion)
|
|
95
|
-
const result = await envelopeDecrypt<
|
|
96
|
-
kek,
|
|
97
|
-
row.ciphertext,
|
|
98
|
-
row.wrappedDek
|
|
99
|
-
)
|
|
95
|
+
const result = await envelopeDecrypt<T>(kek, row.ciphertext, row.wrappedDek)
|
|
100
96
|
await this.logAudit(key, 'read')
|
|
101
97
|
return result
|
|
102
98
|
}
|
|
103
99
|
|
|
104
|
-
async getSecretJSON<R = {}>(key: string): Promise<R> {
|
|
105
|
-
const raw = await this.getSecret(key)
|
|
106
|
-
return JSON.parse(raw) as R
|
|
107
|
-
}
|
|
108
|
-
|
|
109
100
|
async hasSecret(key: string): Promise<boolean> {
|
|
110
101
|
const count = await this.secrets.countDocuments({ _id: key }, { limit: 1 })
|
|
111
102
|
return count > 0
|
|
112
103
|
}
|
|
113
104
|
|
|
114
|
-
async
|
|
115
|
-
const
|
|
116
|
-
const { ciphertext, wrappedDEK } = await envelopeEncrypt(
|
|
117
|
-
this.key,
|
|
118
|
-
plaintext
|
|
119
|
-
)
|
|
105
|
+
async setSecret(key: string, value: unknown): Promise<void> {
|
|
106
|
+
const { ciphertext, wrappedDEK } = await envelopeEncrypt(this.key, value)
|
|
120
107
|
const now = new Date()
|
|
121
108
|
|
|
122
109
|
await this.secrets.updateOne(
|
|
@@ -144,6 +131,26 @@ export class MongoDBSecretService implements SecretService {
|
|
|
144
131
|
await this.logAudit(key, 'delete')
|
|
145
132
|
}
|
|
146
133
|
|
|
134
|
+
async getSecrets(keys: string[]): Promise<Record<string, unknown>> {
|
|
135
|
+
const rows = await this.secrets
|
|
136
|
+
.find({ _id: { $in: keys } } as any)
|
|
137
|
+
.toArray()
|
|
138
|
+
const out: Record<string, unknown> = {}
|
|
139
|
+
for (const row of rows) {
|
|
140
|
+
try {
|
|
141
|
+
const kek = this.getKEK(row.keyVersion)
|
|
142
|
+
out[row._id] = await envelopeDecrypt(
|
|
143
|
+
kek,
|
|
144
|
+
row.ciphertext,
|
|
145
|
+
row.wrappedDek
|
|
146
|
+
)
|
|
147
|
+
} catch {
|
|
148
|
+
// skip secrets that fail to decrypt
|
|
149
|
+
}
|
|
150
|
+
}
|
|
151
|
+
return out
|
|
152
|
+
}
|
|
153
|
+
|
|
147
154
|
async rotateKEK(): Promise<number> {
|
|
148
155
|
if (!this.previousKey) {
|
|
149
156
|
throw new Error('No previousKey configured — nothing to rotate from')
|
|
@@ -71,7 +71,7 @@ function registerTests(name: string, getDb: () => Db) {
|
|
|
71
71
|
auditReads: true,
|
|
72
72
|
})
|
|
73
73
|
await service.init()
|
|
74
|
-
await service.
|
|
74
|
+
await service.setSecret('audit-test', 'value')
|
|
75
75
|
await service.getSecret('audit-test')
|
|
76
76
|
await service.deleteSecret('audit-test')
|
|
77
77
|
|
|
@@ -94,7 +94,7 @@ function registerTests(name: string, getDb: () => Db) {
|
|
|
94
94
|
auditReads: false,
|
|
95
95
|
})
|
|
96
96
|
await service.init()
|
|
97
|
-
await service.
|
|
97
|
+
await service.setSecret('no-read-audit', 'value')
|
|
98
98
|
await service.getSecret('no-read-audit')
|
|
99
99
|
|
|
100
100
|
const logs = await getDb()
|
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import type {
|
|
2
|
+
WorkflowPlannedStep,
|
|
2
3
|
WorkflowRun,
|
|
3
4
|
StepState,
|
|
4
5
|
WorkflowStatus,
|
|
@@ -16,6 +17,8 @@ interface WorkflowRunDoc {
|
|
|
16
17
|
state: Record<string, unknown>
|
|
17
18
|
inline: boolean
|
|
18
19
|
graphHash: string | null
|
|
20
|
+
deterministic?: boolean
|
|
21
|
+
plannedSteps?: WorkflowPlannedStep[]
|
|
19
22
|
wire: any | null
|
|
20
23
|
createdAt: Date
|
|
21
24
|
updatedAt: Date
|
|
@@ -238,6 +241,8 @@ export class MongoDBWorkflowRunService implements WorkflowRunService {
|
|
|
238
241
|
error: row.error ?? undefined,
|
|
239
242
|
inline: row.inline,
|
|
240
243
|
graphHash: row.graphHash ?? undefined,
|
|
244
|
+
deterministic: row.deterministic ?? undefined,
|
|
245
|
+
plannedSteps: row.plannedSteps ?? undefined,
|
|
241
246
|
wire: row.wire ?? { type: 'unknown' },
|
|
242
247
|
createdAt: new Date(row.createdAt),
|
|
243
248
|
updatedAt: new Date(row.updatedAt),
|
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
import type { SerializedError } from '@pikku/core'
|
|
2
2
|
import {
|
|
3
3
|
PikkuWorkflowService,
|
|
4
|
+
type WorkflowPlannedStep,
|
|
4
5
|
type WorkflowRun,
|
|
5
6
|
type WorkflowRunWire,
|
|
6
7
|
type StepState,
|
|
@@ -20,6 +21,8 @@ interface WorkflowRunDoc {
|
|
|
20
21
|
state: Record<string, unknown>
|
|
21
22
|
inline: boolean
|
|
22
23
|
graphHash: string | null
|
|
24
|
+
deterministic?: boolean
|
|
25
|
+
plannedSteps?: WorkflowPlannedStep[]
|
|
23
26
|
wire: any | null
|
|
24
27
|
createdAt: Date
|
|
25
28
|
updatedAt: Date
|
|
@@ -107,12 +110,16 @@ export class MongoDBWorkflowService extends PikkuWorkflowService {
|
|
|
107
110
|
this.initialized = true
|
|
108
111
|
}
|
|
109
112
|
|
|
110
|
-
async
|
|
113
|
+
protected async createRunImpl(
|
|
111
114
|
workflowName: string,
|
|
112
115
|
input: any,
|
|
113
116
|
inline: boolean,
|
|
114
117
|
graphHash: string,
|
|
115
|
-
wire: WorkflowRunWire
|
|
118
|
+
wire: WorkflowRunWire,
|
|
119
|
+
options?: {
|
|
120
|
+
deterministic?: boolean
|
|
121
|
+
plannedSteps?: WorkflowPlannedStep[]
|
|
122
|
+
}
|
|
116
123
|
): Promise<string> {
|
|
117
124
|
const id = crypto.randomUUID()
|
|
118
125
|
const now = new Date()
|
|
@@ -127,6 +134,8 @@ export class MongoDBWorkflowService extends PikkuWorkflowService {
|
|
|
127
134
|
state: {},
|
|
128
135
|
inline,
|
|
129
136
|
graphHash,
|
|
137
|
+
deterministic: options?.deterministic ?? false,
|
|
138
|
+
plannedSteps: options?.plannedSteps ?? [],
|
|
130
139
|
wire,
|
|
131
140
|
createdAt: now,
|
|
132
141
|
updatedAt: now,
|
|
@@ -139,7 +148,7 @@ export class MongoDBWorkflowService extends PikkuWorkflowService {
|
|
|
139
148
|
return this.runService.getRun(id)
|
|
140
149
|
}
|
|
141
150
|
|
|
142
|
-
async
|
|
151
|
+
protected async updateRunStatusImpl(
|
|
143
152
|
id: string,
|
|
144
153
|
status: WorkflowStatus,
|
|
145
154
|
output?: any,
|
|
@@ -158,7 +167,7 @@ export class MongoDBWorkflowService extends PikkuWorkflowService {
|
|
|
158
167
|
)
|
|
159
168
|
}
|
|
160
169
|
|
|
161
|
-
async
|
|
170
|
+
protected async insertStepStateImpl(
|
|
162
171
|
runId: string,
|
|
163
172
|
stepName: string,
|
|
164
173
|
rpcName: string | null,
|
|
@@ -234,7 +243,7 @@ export class MongoDBWorkflowService extends PikkuWorkflowService {
|
|
|
234
243
|
return this.runService.getRunHistory(runId)
|
|
235
244
|
}
|
|
236
245
|
|
|
237
|
-
async
|
|
246
|
+
protected async setStepRunningImpl(stepId: string): Promise<void> {
|
|
238
247
|
await this.steps.updateOne(
|
|
239
248
|
{ _id: stepId },
|
|
240
249
|
{ $set: { status: 'running', updatedAt: new Date() } }
|
|
@@ -254,7 +263,7 @@ export class MongoDBWorkflowService extends PikkuWorkflowService {
|
|
|
254
263
|
}
|
|
255
264
|
}
|
|
256
265
|
|
|
257
|
-
async
|
|
266
|
+
protected async setStepScheduledImpl(stepId: string): Promise<void> {
|
|
258
267
|
await this.steps.updateOne(
|
|
259
268
|
{ _id: stepId },
|
|
260
269
|
{ $set: { status: 'scheduled', updatedAt: new Date() } }
|
|
@@ -304,7 +313,10 @@ export class MongoDBWorkflowService extends PikkuWorkflowService {
|
|
|
304
313
|
}
|
|
305
314
|
}
|
|
306
315
|
|
|
307
|
-
async
|
|
316
|
+
protected async setStepChildRunIdImpl(
|
|
317
|
+
stepId: string,
|
|
318
|
+
childRunId: string
|
|
319
|
+
): Promise<void> {
|
|
308
320
|
await this.steps.updateOne(
|
|
309
321
|
{ _id: stepId },
|
|
310
322
|
{
|
|
@@ -316,7 +328,10 @@ export class MongoDBWorkflowService extends PikkuWorkflowService {
|
|
|
316
328
|
)
|
|
317
329
|
}
|
|
318
330
|
|
|
319
|
-
async
|
|
331
|
+
protected async setStepResultImpl(
|
|
332
|
+
stepId: string,
|
|
333
|
+
result: any
|
|
334
|
+
): Promise<void> {
|
|
320
335
|
await this.steps.updateOne(
|
|
321
336
|
{ _id: stepId },
|
|
322
337
|
{
|
|
@@ -343,7 +358,10 @@ export class MongoDBWorkflowService extends PikkuWorkflowService {
|
|
|
343
358
|
}
|
|
344
359
|
}
|
|
345
360
|
|
|
346
|
-
async
|
|
361
|
+
protected async setStepErrorImpl(
|
|
362
|
+
stepId: string,
|
|
363
|
+
error: Error
|
|
364
|
+
): Promise<void> {
|
|
347
365
|
const serializedError: SerializedError = {
|
|
348
366
|
message: error.message,
|
|
349
367
|
stack: error.stack,
|
|
@@ -376,7 +394,7 @@ export class MongoDBWorkflowService extends PikkuWorkflowService {
|
|
|
376
394
|
}
|
|
377
395
|
}
|
|
378
396
|
|
|
379
|
-
async
|
|
397
|
+
protected async createRetryAttemptImpl(
|
|
380
398
|
stepId: string,
|
|
381
399
|
status: 'pending' | 'running'
|
|
382
400
|
): Promise<StepState> {
|
|
@@ -503,14 +521,17 @@ export class MongoDBWorkflowService extends PikkuWorkflowService {
|
|
|
503
521
|
return results
|
|
504
522
|
}
|
|
505
523
|
|
|
506
|
-
async
|
|
524
|
+
protected async setBranchTakenImpl(
|
|
525
|
+
stepId: string,
|
|
526
|
+
branchKey: string
|
|
527
|
+
): Promise<void> {
|
|
507
528
|
await this.steps.updateOne(
|
|
508
529
|
{ _id: stepId },
|
|
509
530
|
{ $set: { branchTaken: branchKey, updatedAt: new Date() } }
|
|
510
531
|
)
|
|
511
532
|
}
|
|
512
533
|
|
|
513
|
-
async
|
|
534
|
+
protected async updateRunStateImpl(
|
|
514
535
|
runId: string,
|
|
515
536
|
name: string,
|
|
516
537
|
value: unknown
|
|
@@ -538,7 +559,7 @@ export class MongoDBWorkflowService extends PikkuWorkflowService {
|
|
|
538
559
|
return row.state ?? {}
|
|
539
560
|
}
|
|
540
561
|
|
|
541
|
-
async
|
|
562
|
+
protected async upsertWorkflowVersionImpl(
|
|
542
563
|
name: string,
|
|
543
564
|
graphHash: string,
|
|
544
565
|
graph: any,
|
|
@@ -561,7 +582,7 @@ export class MongoDBWorkflowService extends PikkuWorkflowService {
|
|
|
561
582
|
)
|
|
562
583
|
}
|
|
563
584
|
|
|
564
|
-
async
|
|
585
|
+
protected async updateWorkflowVersionStatusImpl(
|
|
565
586
|
name: string,
|
|
566
587
|
graphHash: string,
|
|
567
588
|
status: WorkflowVersionStatus
|