@poncho-ai/harness 0.25.0 → 0.27.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/.turbo/turbo-build.log +5 -5
- package/CHANGELOG.md +28 -0
- package/dist/index.d.ts +41 -8
- package/dist/index.js +603 -359
- package/package.json +2 -2
- package/src/config.ts +4 -0
- package/src/harness.ts +41 -24
- package/src/kv-store.ts +216 -0
- package/src/memory.ts +26 -291
- package/src/state.ts +74 -9
- package/src/subagent-manager.ts +6 -2
- package/src/subagent-tools.ts +21 -48
- package/src/todo-tools.ts +363 -0
- package/.turbo/turbo-lint.log +0 -6
- package/.turbo/turbo-test.log +0 -135
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@poncho-ai/harness",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.27.0",
|
|
4
4
|
"description": "Agent execution runtime - conversation loop, tool dispatch, streaming",
|
|
5
5
|
"repository": {
|
|
6
6
|
"type": "git",
|
|
@@ -31,7 +31,7 @@
|
|
|
31
31
|
"redis": "^5.10.0",
|
|
32
32
|
"yaml": "^2.4.0",
|
|
33
33
|
"zod": "^3.22.0",
|
|
34
|
-
"@poncho-ai/sdk": "1.
|
|
34
|
+
"@poncho-ai/sdk": "1.6.0"
|
|
35
35
|
},
|
|
36
36
|
"devDependencies": {
|
|
37
37
|
"@types/mustache": "^4.2.6",
|
package/src/config.ts
CHANGED
|
@@ -42,6 +42,10 @@ export type BuiltInToolToggles = {
|
|
|
42
42
|
edit_file?: boolean;
|
|
43
43
|
delete_file?: boolean;
|
|
44
44
|
delete_directory?: boolean;
|
|
45
|
+
todo_list?: boolean;
|
|
46
|
+
todo_add?: boolean;
|
|
47
|
+
todo_update?: boolean;
|
|
48
|
+
todo_remove?: boolean;
|
|
45
49
|
};
|
|
46
50
|
|
|
47
51
|
export interface MessagingChannelConfig {
|
package/src/harness.ts
CHANGED
|
@@ -16,13 +16,14 @@ import { getTextContent } from "@poncho-ai/sdk";
|
|
|
16
16
|
import type { UploadStore } from "./upload-store.js";
|
|
17
17
|
import { PONCHO_UPLOAD_SCHEME, deriveUploadKey } from "./upload-store.js";
|
|
18
18
|
import { parseAgentFile, parseAgentMarkdown, renderAgentPrompt, type ParsedAgent, type AgentFrontmatter } from "./agent-parser.js";
|
|
19
|
-
import { loadPonchoConfig, resolveMemoryConfig, type PonchoConfig, type ToolAccess, type BuiltInToolToggles } from "./config.js";
|
|
19
|
+
import { loadPonchoConfig, resolveMemoryConfig, resolveStateConfig, type PonchoConfig, type ToolAccess, type BuiltInToolToggles } from "./config.js";
|
|
20
20
|
import { createDefaultTools, createDeleteDirectoryTool, createDeleteTool, createEditTool, createWriteTool, ponchoDocsTool } from "./default-tools.js";
|
|
21
21
|
import {
|
|
22
22
|
createMemoryStore,
|
|
23
23
|
createMemoryTools,
|
|
24
24
|
type MemoryStore,
|
|
25
25
|
} from "./memory.js";
|
|
26
|
+
import { createTodoStore, createTodoTools, type TodoItem, type TodoStore } from "./todo-tools.js";
|
|
26
27
|
import { LocalMcpBridge } from "./mcp.js";
|
|
27
28
|
import { createModelProvider, getModelContextWindow, type ModelProviderFactory, type ProviderConfig } from "./model-factory.js";
|
|
28
29
|
import { buildSkillContextWindow, loadSkillMetadata } from "./skill-context.js";
|
|
@@ -550,6 +551,7 @@ export class AgentHarness {
|
|
|
550
551
|
readonly uploadStore?: UploadStore;
|
|
551
552
|
private skillContextWindow = "";
|
|
552
553
|
private memoryStore?: MemoryStore;
|
|
554
|
+
private todoStore?: TodoStore;
|
|
553
555
|
private loadedConfig?: PonchoConfig;
|
|
554
556
|
private loadedSkills: SkillMetadata[] = [];
|
|
555
557
|
private skillFingerprint = "";
|
|
@@ -560,7 +562,7 @@ export class AgentHarness {
|
|
|
560
562
|
private insideTelemetryCapture = false;
|
|
561
563
|
private _browserSession?: unknown;
|
|
562
564
|
private _browserMod?: {
|
|
563
|
-
createBrowserTools: (getSession: () => unknown
|
|
565
|
+
createBrowserTools: (getSession: () => unknown) => ToolDefinition[];
|
|
564
566
|
BrowserSession: new (sessionId: string, config: Record<string, unknown>) => unknown;
|
|
565
567
|
};
|
|
566
568
|
|
|
@@ -620,11 +622,7 @@ export class AgentHarness {
|
|
|
620
622
|
setSubagentManager(manager: SubagentManager): void {
|
|
621
623
|
this.subagentManager = manager;
|
|
622
624
|
this.dispatcher.registerMany(
|
|
623
|
-
createSubagentTools(
|
|
624
|
-
manager,
|
|
625
|
-
() => this._currentRunConversationId,
|
|
626
|
-
() => this._currentRunOwnerId ?? "anonymous",
|
|
627
|
-
),
|
|
625
|
+
createSubagentTools(manager),
|
|
628
626
|
);
|
|
629
627
|
}
|
|
630
628
|
|
|
@@ -678,6 +676,11 @@ export class AgentHarness {
|
|
|
678
676
|
return this.parsedAgent?.frontmatter;
|
|
679
677
|
}
|
|
680
678
|
|
|
679
|
+
async getTodos(conversationId: string): Promise<TodoItem[]> {
|
|
680
|
+
if (!this.todoStore) return [];
|
|
681
|
+
return this.todoStore.get(conversationId);
|
|
682
|
+
}
|
|
683
|
+
|
|
681
684
|
private listActiveSkills(): string[] {
|
|
682
685
|
return [...this.activeSkillNames].sort();
|
|
683
686
|
}
|
|
@@ -1008,8 +1011,9 @@ export class AgentHarness {
|
|
|
1008
1011
|
this.skillContextWindow = buildSkillContextWindow(skillMetadata);
|
|
1009
1012
|
this.skillFingerprint = this.buildSkillFingerprint(skillMetadata);
|
|
1010
1013
|
this.registerSkillTools(skillMetadata);
|
|
1014
|
+
const agentId = this.parsedAgent.frontmatter.id ?? this.parsedAgent.frontmatter.name;
|
|
1015
|
+
|
|
1011
1016
|
if (memoryConfig?.enabled) {
|
|
1012
|
-
const agentId = this.parsedAgent.frontmatter.id ?? this.parsedAgent.frontmatter.name;
|
|
1013
1017
|
this.memoryStore = createMemoryStore(
|
|
1014
1018
|
agentId,
|
|
1015
1019
|
memoryConfig,
|
|
@@ -1022,6 +1026,14 @@ export class AgentHarness {
|
|
|
1022
1026
|
);
|
|
1023
1027
|
}
|
|
1024
1028
|
|
|
1029
|
+
const stateConfig = resolveStateConfig(config);
|
|
1030
|
+
this.todoStore = createTodoStore(agentId, stateConfig, { workingDir: this.workingDir });
|
|
1031
|
+
for (const tool of createTodoTools(this.todoStore)) {
|
|
1032
|
+
if (this.isToolEnabled(tool.name)) {
|
|
1033
|
+
this.registerIfMissing(tool);
|
|
1034
|
+
}
|
|
1035
|
+
}
|
|
1036
|
+
|
|
1025
1037
|
if (config?.browser) {
|
|
1026
1038
|
await this.initBrowserTools(config)
|
|
1027
1039
|
.catch((e) => {
|
|
@@ -1149,7 +1161,7 @@ export class AgentHarness {
|
|
|
1149
1161
|
private async initBrowserTools(config: PonchoConfig): Promise<void> {
|
|
1150
1162
|
const spec = ["@poncho-ai", "browser"].join("/");
|
|
1151
1163
|
let browserMod: {
|
|
1152
|
-
createBrowserTools: (getSession: () => unknown
|
|
1164
|
+
createBrowserTools: (getSession: () => unknown) => ToolDefinition[];
|
|
1153
1165
|
BrowserSession: new (sessionId: string, cfg?: Record<string, unknown>) => unknown;
|
|
1154
1166
|
};
|
|
1155
1167
|
try {
|
|
@@ -1193,7 +1205,6 @@ export class AgentHarness {
|
|
|
1193
1205
|
|
|
1194
1206
|
const tools = browserMod.createBrowserTools(
|
|
1195
1207
|
() => session,
|
|
1196
|
-
() => this._currentRunConversationId ?? "__default__",
|
|
1197
1208
|
);
|
|
1198
1209
|
for (const tool of tools) {
|
|
1199
1210
|
if (this.isToolEnabled(tool.name)) {
|
|
@@ -1202,10 +1213,6 @@ export class AgentHarness {
|
|
|
1202
1213
|
}
|
|
1203
1214
|
}
|
|
1204
1215
|
|
|
1205
|
-
/** Conversation ID of the currently executing run (set during run, cleared after). */
|
|
1206
|
-
private _currentRunConversationId?: string;
|
|
1207
|
-
/** Owner ID of the currently executing run (used by subagent tools). */
|
|
1208
|
-
private _currentRunOwnerId?: string;
|
|
1209
1216
|
|
|
1210
1217
|
get browserSession(): unknown {
|
|
1211
1218
|
return this._browserSession;
|
|
@@ -1353,13 +1360,6 @@ export class AgentHarness {
|
|
|
1353
1360
|
await this.refreshAgentIfChanged();
|
|
1354
1361
|
await this.refreshSkillsIfChanged();
|
|
1355
1362
|
|
|
1356
|
-
// Track which conversation/owner this run belongs to so browser & subagent tools resolve correctly
|
|
1357
|
-
this._currentRunConversationId = input.conversationId;
|
|
1358
|
-
const ownerParam = input.parameters?.__ownerId;
|
|
1359
|
-
if (typeof ownerParam === "string") {
|
|
1360
|
-
this._currentRunOwnerId = ownerParam;
|
|
1361
|
-
}
|
|
1362
|
-
|
|
1363
1363
|
let agent = this.parsedAgent as ParsedAgent;
|
|
1364
1364
|
const runId = `run_${randomUUID()}`;
|
|
1365
1365
|
const start = now();
|
|
@@ -1369,9 +1369,9 @@ export class AgentHarness {
|
|
|
1369
1369
|
? 0 // no hard timeout in development unless explicitly configured
|
|
1370
1370
|
: (configuredTimeout ?? 300) * 1000;
|
|
1371
1371
|
const platformMaxDurationSec = Number(process.env.PONCHO_MAX_DURATION) || 0;
|
|
1372
|
-
const softDeadlineMs = platformMaxDurationSec
|
|
1373
|
-
?
|
|
1374
|
-
:
|
|
1372
|
+
const softDeadlineMs = (input.disableSoftDeadline || platformMaxDurationSec <= 0)
|
|
1373
|
+
? 0
|
|
1374
|
+
: platformMaxDurationSec * 800;
|
|
1375
1375
|
const messages: Message[] = [...(input.messages ?? [])];
|
|
1376
1376
|
const inputMessageCount = messages.length;
|
|
1377
1377
|
const events: AgentEvent[] = [];
|
|
@@ -1525,6 +1525,18 @@ ${boundedMainMemory.trim()}`
|
|
|
1525
1525
|
metadata: { timestamp: now(), id: randomUUID() },
|
|
1526
1526
|
});
|
|
1527
1527
|
}
|
|
1528
|
+
} else {
|
|
1529
|
+
// Continuation run (no explicit task). Some providers (Anthropic) require
|
|
1530
|
+
// the conversation to end with a user message. Inject a transient signal
|
|
1531
|
+
// that is sent to the LLM but never persisted to the conversation store.
|
|
1532
|
+
const lastMsg = messages[messages.length - 1];
|
|
1533
|
+
if (lastMsg && lastMsg.role !== "user") {
|
|
1534
|
+
messages.push({
|
|
1535
|
+
role: "user",
|
|
1536
|
+
content: "[System: Your previous turn was interrupted by a time limit. Continue from where you left off — do NOT repeat what you already said. Proceed directly with the next action or tool call.]",
|
|
1537
|
+
metadata: { timestamp: now(), id: randomUUID() },
|
|
1538
|
+
});
|
|
1539
|
+
}
|
|
1528
1540
|
}
|
|
1529
1541
|
|
|
1530
1542
|
let responseText = "";
|
|
@@ -1561,6 +1573,7 @@ ${boundedMainMemory.trim()}`
|
|
|
1561
1573
|
tokens: { input: totalInputTokens, output: totalOutputTokens, cached: totalCachedTokens },
|
|
1562
1574
|
duration: now() - start,
|
|
1563
1575
|
continuation: true,
|
|
1576
|
+
continuationMessages: [...messages],
|
|
1564
1577
|
maxSteps,
|
|
1565
1578
|
};
|
|
1566
1579
|
yield pushEvent({ type: "run:completed", runId, result });
|
|
@@ -1711,6 +1724,9 @@ ${boundedMainMemory.trim()}`
|
|
|
1711
1724
|
} catch {
|
|
1712
1725
|
// Not JSON, treat as regular assistant text.
|
|
1713
1726
|
}
|
|
1727
|
+
if (!assistantText || assistantText.trim().length === 0) {
|
|
1728
|
+
return [];
|
|
1729
|
+
}
|
|
1714
1730
|
return [{ role: "assistant" as const, content: assistantText }];
|
|
1715
1731
|
}
|
|
1716
1732
|
|
|
@@ -2387,6 +2403,7 @@ ${boundedMainMemory.trim()}`
|
|
|
2387
2403
|
tokens: { input: totalInputTokens, output: totalOutputTokens, cached: totalCachedTokens },
|
|
2388
2404
|
duration: now() - start,
|
|
2389
2405
|
continuation: true,
|
|
2406
|
+
continuationMessages: [...messages],
|
|
2390
2407
|
maxSteps,
|
|
2391
2408
|
};
|
|
2392
2409
|
yield pushEvent({ type: "run:completed", runId, result });
|
package/src/kv-store.ts
ADDED
|
@@ -0,0 +1,216 @@
|
|
|
1
|
+
import type { StateConfig } from "./state.js";
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* Minimal raw key-value interface shared by MemoryStore, TodoStore, and any
|
|
5
|
+
* future stores that sit on top of the same user-configured backend.
|
|
6
|
+
*/
|
|
7
|
+
export interface RawKVStore {
|
|
8
|
+
get(key: string): Promise<string | undefined>;
|
|
9
|
+
set(key: string, value: string): Promise<void>;
|
|
10
|
+
setWithTtl(key: string, value: string, ttlSeconds: number): Promise<void>;
|
|
11
|
+
}
|
|
12
|
+
|
|
13
|
+
// ---------------------------------------------------------------------------
|
|
14
|
+
// Upstash
|
|
15
|
+
// ---------------------------------------------------------------------------
|
|
16
|
+
|
|
17
|
+
class UpstashKVStore implements RawKVStore {
|
|
18
|
+
private readonly baseUrl: string;
|
|
19
|
+
private readonly token: string;
|
|
20
|
+
|
|
21
|
+
constructor(baseUrl: string, token: string) {
|
|
22
|
+
this.baseUrl = baseUrl.replace(/\/+$/, "");
|
|
23
|
+
this.token = token;
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
private headers(): HeadersInit {
|
|
27
|
+
return { Authorization: `Bearer ${this.token}`, "Content-Type": "application/json" };
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
async get(key: string): Promise<string | undefined> {
|
|
31
|
+
const response = await fetch(`${this.baseUrl}/get/${encodeURIComponent(key)}`, {
|
|
32
|
+
method: "POST",
|
|
33
|
+
headers: this.headers(),
|
|
34
|
+
});
|
|
35
|
+
if (!response.ok) return undefined;
|
|
36
|
+
const payload = (await response.json()) as { result?: string | null };
|
|
37
|
+
return payload.result ?? undefined;
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
async set(key: string, value: string): Promise<void> {
|
|
41
|
+
const response = await fetch(this.baseUrl, {
|
|
42
|
+
method: "POST",
|
|
43
|
+
headers: this.headers(),
|
|
44
|
+
body: JSON.stringify(["SET", key, value]),
|
|
45
|
+
});
|
|
46
|
+
if (!response.ok) {
|
|
47
|
+
const text = await response.text().catch(() => "");
|
|
48
|
+
console.error(`[kv][upstash] SET failed (${response.status}): ${text.slice(0, 200)}`);
|
|
49
|
+
}
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
async setWithTtl(key: string, value: string, ttl: number): Promise<void> {
|
|
53
|
+
const response = await fetch(this.baseUrl, {
|
|
54
|
+
method: "POST",
|
|
55
|
+
headers: this.headers(),
|
|
56
|
+
body: JSON.stringify(["SETEX", key, Math.max(1, ttl), value]),
|
|
57
|
+
});
|
|
58
|
+
if (!response.ok) {
|
|
59
|
+
const text = await response.text().catch(() => "");
|
|
60
|
+
console.error(`[kv][upstash] SETEX failed (${response.status}): ${text.slice(0, 200)}`);
|
|
61
|
+
}
|
|
62
|
+
}
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
// ---------------------------------------------------------------------------
|
|
66
|
+
// Redis
|
|
67
|
+
// ---------------------------------------------------------------------------
|
|
68
|
+
|
|
69
|
+
class RedisKVStore implements RawKVStore {
|
|
70
|
+
private readonly clientPromise: Promise<
|
|
71
|
+
| {
|
|
72
|
+
get: (key: string) => Promise<string | null>;
|
|
73
|
+
set: (key: string, value: string, options?: { EX?: number }) => Promise<unknown>;
|
|
74
|
+
}
|
|
75
|
+
| undefined
|
|
76
|
+
>;
|
|
77
|
+
|
|
78
|
+
constructor(url: string) {
|
|
79
|
+
this.clientPromise = (async () => {
|
|
80
|
+
try {
|
|
81
|
+
const redisModule = (await import("redis")) as unknown as {
|
|
82
|
+
createClient: (args: { url: string }) => {
|
|
83
|
+
connect: () => Promise<unknown>;
|
|
84
|
+
get: (key: string) => Promise<string | null>;
|
|
85
|
+
set: (key: string, value: string, options?: { EX?: number }) => Promise<unknown>;
|
|
86
|
+
};
|
|
87
|
+
};
|
|
88
|
+
const client = redisModule.createClient({ url });
|
|
89
|
+
await client.connect();
|
|
90
|
+
return client;
|
|
91
|
+
} catch {
|
|
92
|
+
return undefined;
|
|
93
|
+
}
|
|
94
|
+
})();
|
|
95
|
+
}
|
|
96
|
+
|
|
97
|
+
async get(key: string): Promise<string | undefined> {
|
|
98
|
+
const client = await this.clientPromise;
|
|
99
|
+
if (!client) throw new Error("Redis unavailable");
|
|
100
|
+
const value = await client.get(key);
|
|
101
|
+
return value ?? undefined;
|
|
102
|
+
}
|
|
103
|
+
|
|
104
|
+
async set(key: string, value: string): Promise<void> {
|
|
105
|
+
const client = await this.clientPromise;
|
|
106
|
+
if (!client) throw new Error("Redis unavailable");
|
|
107
|
+
await client.set(key, value);
|
|
108
|
+
}
|
|
109
|
+
|
|
110
|
+
async setWithTtl(key: string, value: string, ttl: number): Promise<void> {
|
|
111
|
+
const client = await this.clientPromise;
|
|
112
|
+
if (!client) throw new Error("Redis unavailable");
|
|
113
|
+
await client.set(key, value, { EX: Math.max(1, ttl) });
|
|
114
|
+
}
|
|
115
|
+
}
|
|
116
|
+
|
|
117
|
+
// ---------------------------------------------------------------------------
|
|
118
|
+
// DynamoDB
|
|
119
|
+
// ---------------------------------------------------------------------------
|
|
120
|
+
|
|
121
|
+
class DynamoDbKVStore implements RawKVStore {
|
|
122
|
+
private readonly table: string;
|
|
123
|
+
private readonly clientPromise: Promise<
|
|
124
|
+
| {
|
|
125
|
+
send: (command: unknown) => Promise<unknown>;
|
|
126
|
+
GetItemCommand: new (input: unknown) => unknown;
|
|
127
|
+
PutItemCommand: new (input: unknown) => unknown;
|
|
128
|
+
}
|
|
129
|
+
| undefined
|
|
130
|
+
>;
|
|
131
|
+
|
|
132
|
+
constructor(table: string, region?: string) {
|
|
133
|
+
this.table = table;
|
|
134
|
+
this.clientPromise = (async () => {
|
|
135
|
+
try {
|
|
136
|
+
const module = (await import("@aws-sdk/client-dynamodb")) as {
|
|
137
|
+
DynamoDBClient: new (input: { region?: string }) => {
|
|
138
|
+
send: (command: unknown) => Promise<unknown>;
|
|
139
|
+
};
|
|
140
|
+
GetItemCommand: new (input: unknown) => unknown;
|
|
141
|
+
PutItemCommand: new (input: unknown) => unknown;
|
|
142
|
+
};
|
|
143
|
+
const client = new module.DynamoDBClient({ region });
|
|
144
|
+
return {
|
|
145
|
+
send: client.send.bind(client),
|
|
146
|
+
GetItemCommand: module.GetItemCommand,
|
|
147
|
+
PutItemCommand: module.PutItemCommand,
|
|
148
|
+
};
|
|
149
|
+
} catch {
|
|
150
|
+
return undefined;
|
|
151
|
+
}
|
|
152
|
+
})();
|
|
153
|
+
}
|
|
154
|
+
|
|
155
|
+
async get(key: string): Promise<string | undefined> {
|
|
156
|
+
const client = await this.clientPromise;
|
|
157
|
+
if (!client) throw new Error("DynamoDB unavailable");
|
|
158
|
+
const result = (await client.send(
|
|
159
|
+
new client.GetItemCommand({ TableName: this.table, Key: { runId: { S: key } } }),
|
|
160
|
+
)) as { Item?: { value?: { S?: string } } };
|
|
161
|
+
return result.Item?.value?.S;
|
|
162
|
+
}
|
|
163
|
+
|
|
164
|
+
async set(key: string, value: string): Promise<void> {
|
|
165
|
+
const client = await this.clientPromise;
|
|
166
|
+
if (!client) throw new Error("DynamoDB unavailable");
|
|
167
|
+
await client.send(
|
|
168
|
+
new client.PutItemCommand({
|
|
169
|
+
TableName: this.table,
|
|
170
|
+
Item: { runId: { S: key }, value: { S: value } },
|
|
171
|
+
}),
|
|
172
|
+
);
|
|
173
|
+
}
|
|
174
|
+
|
|
175
|
+
async setWithTtl(key: string, value: string, ttl: number): Promise<void> {
|
|
176
|
+
const client = await this.clientPromise;
|
|
177
|
+
if (!client) throw new Error("DynamoDB unavailable");
|
|
178
|
+
const ttlEpoch = Math.floor(Date.now() / 1000) + Math.max(1, ttl);
|
|
179
|
+
await client.send(
|
|
180
|
+
new client.PutItemCommand({
|
|
181
|
+
TableName: this.table,
|
|
182
|
+
Item: { runId: { S: key }, value: { S: value }, ttl: { N: String(ttlEpoch) } },
|
|
183
|
+
}),
|
|
184
|
+
);
|
|
185
|
+
}
|
|
186
|
+
}
|
|
187
|
+
|
|
188
|
+
// ---------------------------------------------------------------------------
|
|
189
|
+
// Factory — resolves the user's storage config into a RawKVStore, or
|
|
190
|
+
// undefined when the provider is "local" or "memory" (handled by callers).
|
|
191
|
+
// ---------------------------------------------------------------------------
|
|
192
|
+
|
|
193
|
+
export const createRawKVStore = (config?: StateConfig): RawKVStore | undefined => {
|
|
194
|
+
const provider = config?.provider ?? "local";
|
|
195
|
+
|
|
196
|
+
if (provider === "upstash") {
|
|
197
|
+
const urlEnv = config?.urlEnv ?? (process.env.UPSTASH_REDIS_REST_URL ? "UPSTASH_REDIS_REST_URL" : "KV_REST_API_URL");
|
|
198
|
+
const tokenEnv = config?.tokenEnv ?? (process.env.UPSTASH_REDIS_REST_TOKEN ? "UPSTASH_REDIS_REST_TOKEN" : "KV_REST_API_TOKEN");
|
|
199
|
+
const url = process.env[urlEnv] ?? "";
|
|
200
|
+
const token = process.env[tokenEnv] ?? "";
|
|
201
|
+
if (url && token) return new UpstashKVStore(url, token);
|
|
202
|
+
}
|
|
203
|
+
|
|
204
|
+
if (provider === "redis") {
|
|
205
|
+
const urlEnv = config?.urlEnv ?? "REDIS_URL";
|
|
206
|
+
const url = process.env[urlEnv] ?? "";
|
|
207
|
+
if (url) return new RedisKVStore(url);
|
|
208
|
+
}
|
|
209
|
+
|
|
210
|
+
if (provider === "dynamodb") {
|
|
211
|
+
const table = config?.table ?? process.env.PONCHO_DYNAMODB_TABLE ?? "";
|
|
212
|
+
if (table) return new DynamoDbKVStore(table, config?.region);
|
|
213
|
+
}
|
|
214
|
+
|
|
215
|
+
return undefined;
|
|
216
|
+
};
|