open-managed-agents 0.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/LICENSE +93 -0
- package/README.md +248 -0
- package/bin/oma.mjs +499 -0
- package/dist/scripts/alpha-openai-compatible-fixture.mjs +134 -0
- package/dist/scripts/alpha-smoke-models.mjs +92 -0
- package/dist/scripts/alpha-smoke.mjs +581 -0
- package/dist/scripts/oma-doctor.js +230 -0
- package/dist/scripts/oma-models.js +299 -0
- package/dist/scripts/oma-workspaces.js +133 -0
- package/dist/src/control-plane/admin/auth.js +43 -0
- package/dist/src/control-plane/admin/routes.js +76 -0
- package/dist/src/control-plane/admin/service.js +102 -0
- package/dist/src/control-plane/admission.js +90 -0
- package/dist/src/control-plane/agents/routes.js +63 -0
- package/dist/src/control-plane/agents/service.js +687 -0
- package/dist/src/control-plane/agents/store.js +391 -0
- package/dist/src/control-plane/agents/types.js +1 -0
- package/dist/src/control-plane/api-constants.js +3 -0
- package/dist/src/control-plane/app.js +907 -0
- package/dist/src/control-plane/console/auth.js +68 -0
- package/dist/src/control-plane/console/static.js +112 -0
- package/dist/src/control-plane/deployment-runtime-config.js +328 -0
- package/dist/src/control-plane/deployment-runtime-event-coordinator.js +25 -0
- package/dist/src/control-plane/deployment-runtime-turn-guard.js +14 -0
- package/dist/src/control-plane/deployment-session-coordinator.js +25 -0
- package/dist/src/control-plane/deployment-session-output-coordinator.js +27 -0
- package/dist/src/control-plane/deployment-storage.js +354 -0
- package/dist/src/control-plane/egress/image.js +48 -0
- package/dist/src/control-plane/egress/policy.js +571 -0
- package/dist/src/control-plane/egress/presets.js +77 -0
- package/dist/src/control-plane/egress/proxy.js +72 -0
- package/dist/src/control-plane/egress/ssrf.js +106 -0
- package/dist/src/control-plane/egress/utils/debug.js +14 -0
- package/dist/src/control-plane/egress/vendor/http-proxy.js +335 -0
- package/dist/src/control-plane/egress/vendor/mitm-ca.js +226 -0
- package/dist/src/control-plane/egress/vendor/mitm-leaf.js +124 -0
- package/dist/src/control-plane/egress/vendor/parent-proxy.js +438 -0
- package/dist/src/control-plane/egress/vendor/request-filter.js +105 -0
- package/dist/src/control-plane/egress/vendor/sandbox-config.js +1 -0
- package/dist/src/control-plane/egress/vendor/tls-terminate-proxy.js +269 -0
- package/dist/src/control-plane/environments/routes.js +54 -0
- package/dist/src/control-plane/environments/service.js +112 -0
- package/dist/src/control-plane/environments/store.js +132 -0
- package/dist/src/control-plane/environments/types.js +1 -0
- package/dist/src/control-plane/errors.js +81 -0
- package/dist/src/control-plane/events/broadcaster.js +217 -0
- package/dist/src/control-plane/events/constants.js +2 -0
- package/dist/src/control-plane/events/persist.js +39 -0
- package/dist/src/control-plane/events/request.js +190 -0
- package/dist/src/control-plane/events/routes.js +159 -0
- package/dist/src/control-plane/events/runtime-helpers.js +134 -0
- package/dist/src/control-plane/events/service.js +1985 -0
- package/dist/src/control-plane/events/session-guards.js +29 -0
- package/dist/src/control-plane/events/sse.js +6 -0
- package/dist/src/control-plane/events/store.js +772 -0
- package/dist/src/control-plane/events/tool-persistence.js +102 -0
- package/dist/src/control-plane/events/types.js +21 -0
- package/dist/src/control-plane/files/routes.js +121 -0
- package/dist/src/control-plane/files/service.js +65 -0
- package/dist/src/control-plane/files/store-common.js +87 -0
- package/dist/src/control-plane/files/store-local.js +488 -0
- package/dist/src/control-plane/files/store-memory.js +270 -0
- package/dist/src/control-plane/files/store.js +2 -0
- package/dist/src/control-plane/files/types.js +7 -0
- package/dist/src/control-plane/http.js +25 -0
- package/dist/src/control-plane/ids.js +43 -0
- package/dist/src/control-plane/logging.js +263 -0
- package/dist/src/control-plane/models/auth-storage-backend.js +238 -0
- package/dist/src/control-plane/models/catalog.js +78 -0
- package/dist/src/control-plane/models/config-security.js +376 -0
- package/dist/src/control-plane/models/deployment-config.js +93 -0
- package/dist/src/control-plane/models/routes.js +32 -0
- package/dist/src/control-plane/models/service.js +138 -0
- package/dist/src/control-plane/observability/instruments.js +65 -0
- package/dist/src/control-plane/observability/metrics.js +125 -0
- package/dist/src/control-plane/observability/routes.js +95 -0
- package/dist/src/control-plane/openapi/document.js +282 -0
- package/dist/src/control-plane/openapi/routes.js +78 -0
- package/dist/src/control-plane/request-idempotency.js +85 -0
- package/dist/src/control-plane/secrets/envelope.js +132 -0
- package/dist/src/control-plane/secrets/master-key.js +44 -0
- package/dist/src/control-plane/secrets/routes.js +20 -0
- package/dist/src/control-plane/secrets/service.js +71 -0
- package/dist/src/control-plane/secrets/store.js +212 -0
- package/dist/src/control-plane/secrets/types.js +3 -0
- package/dist/src/control-plane/sessions/pi/custom-tools.js +146 -0
- package/dist/src/control-plane/sessions/pi/mcp/bridge.js +527 -0
- package/dist/src/control-plane/sessions/pi/mcp/client.js +157 -0
- package/dist/src/control-plane/sessions/pi/mcp/credential.js +28 -0
- package/dist/src/control-plane/sessions/pi/mcp/fetch.js +69 -0
- package/dist/src/control-plane/sessions/pi/mcp/probe.js +202 -0
- package/dist/src/control-plane/sessions/pi/mcp/runtime.js +13 -0
- package/dist/src/control-plane/sessions/pi/runner.js +1104 -0
- package/dist/src/control-plane/sessions/pi/sandbox/cma-glob.js +388 -0
- package/dist/src/control-plane/sessions/pi/sandbox/cma-grep.js +124 -0
- package/dist/src/control-plane/sessions/pi/sandbox/docker-egress.js +347 -0
- package/dist/src/control-plane/sessions/pi/sandbox/docker.js +1589 -0
- package/dist/src/control-plane/sessions/pi/sandbox/glob.js +48 -0
- package/dist/src/control-plane/sessions/pi/sandbox/image.js +7 -0
- package/dist/src/control-plane/sessions/pi/sandbox/microsandbox.js +1677 -0
- package/dist/src/control-plane/sessions/pi/sandbox/provider.js +561 -0
- package/dist/src/control-plane/sessions/pi/sandbox/selection.js +168 -0
- package/dist/src/control-plane/sessions/pi/span-normalizer.js +82 -0
- package/dist/src/control-plane/sessions/pi/tool-permissions.js +392 -0
- package/dist/src/control-plane/sessions/pi/translator.js +100 -0
- package/dist/src/control-plane/sessions/request.js +164 -0
- package/dist/src/control-plane/sessions/resources.js +63 -0
- package/dist/src/control-plane/sessions/routes.js +88 -0
- package/dist/src/control-plane/sessions/serialize.js +24 -0
- package/dist/src/control-plane/sessions/service.js +733 -0
- package/dist/src/control-plane/sessions/store.js +538 -0
- package/dist/src/control-plane/sessions/types.js +1 -0
- package/dist/src/control-plane/skills/archive.js +169 -0
- package/dist/src/control-plane/skills/routes.js +41 -0
- package/dist/src/control-plane/skills/service.js +29 -0
- package/dist/src/control-plane/skills/store.js +233 -0
- package/dist/src/control-plane/skills/types.js +5 -0
- package/dist/src/control-plane/sqlite-transaction.js +15 -0
- package/dist/src/control-plane/vaults/mcp-oauth-validate.js +165 -0
- package/dist/src/control-plane/vaults/oauth-refresh-ticker.js +43 -0
- package/dist/src/control-plane/vaults/oauth-refresh.js +448 -0
- package/dist/src/control-plane/vaults/routes.js +66 -0
- package/dist/src/control-plane/vaults/service.js +553 -0
- package/dist/src/control-plane/vaults/store.js +691 -0
- package/dist/src/control-plane/vaults/types.js +1 -0
- package/dist/src/control-plane/wake-loop.js +65 -0
- package/dist/src/control-plane/wiring.js +92 -0
- package/dist/src/control-plane/workspace.js +4 -0
- package/dist/src/control-plane/workspaces/store.js +158 -0
- package/dist/src/egress-proxy-main.js +103 -0
- package/dist/src/main.js +124 -0
- package/dist/src/types/agents.js +1 -0
- package/dist/src/types/common.js +1 -0
- package/dist/src/types/environments.js +1 -0
- package/dist/src/types/events.js +121 -0
- package/dist/src/types/files.js +1 -0
- package/dist/src/types/json.js +19 -0
- package/dist/src/types/sessions.js +1 -0
- package/dist/ui/managed-agents-console/README.md +52 -0
- package/dist/ui/managed-agents-console/docs/agents.md +23 -0
- package/dist/ui/managed-agents-console/docs/console.md +23 -0
- package/dist/ui/managed-agents-console/docs/dreams.md +5 -0
- package/dist/ui/managed-agents-console/docs/environments.md +27 -0
- package/dist/ui/managed-agents-console/docs/events.md +19 -0
- package/dist/ui/managed-agents-console/docs/files.md +17 -0
- package/dist/ui/managed-agents-console/docs/github.md +11 -0
- package/dist/ui/managed-agents-console/docs/integrations.md +19 -0
- package/dist/ui/managed-agents-console/docs/memory.md +11 -0
- package/dist/ui/managed-agents-console/docs/migration.md +17 -0
- package/dist/ui/managed-agents-console/docs/multiagent.md +11 -0
- package/dist/ui/managed-agents-console/docs/outcomes.md +11 -0
- package/dist/ui/managed-agents-console/docs/overview.md +41 -0
- package/dist/ui/managed-agents-console/docs/permissions.md +24 -0
- package/dist/ui/managed-agents-console/docs/quickstart.md +51 -0
- package/dist/ui/managed-agents-console/docs/reference.md +27 -0
- package/dist/ui/managed-agents-console/docs/sandbox-reference.md +19 -0
- package/dist/ui/managed-agents-console/docs/sandbox-security.md +15 -0
- package/dist/ui/managed-agents-console/docs/scheduled-deployments.md +5 -0
- package/dist/ui/managed-agents-console/docs/self-hosted-sandboxes.md +15 -0
- package/dist/ui/managed-agents-console/docs/session-operations.md +21 -0
- package/dist/ui/managed-agents-console/docs/sessions.md +21 -0
- package/dist/ui/managed-agents-console/docs/skills.md +15 -0
- package/dist/ui/managed-agents-console/docs/tools.md +23 -0
- package/dist/ui/managed-agents-console/docs/vaults.md +19 -0
- package/dist/ui/managed-agents-console/docs/webhooks.md +11 -0
- package/dist/ui/managed-agents-console/index.html +47 -0
- package/dist/ui/managed-agents-console/serve.mjs +116 -0
- package/dist/ui/managed-agents-console/src/agents-files.jsx +273 -0
- package/dist/ui/managed-agents-console/src/api.js +1043 -0
- package/dist/ui/managed-agents-console/src/app.jsx +633 -0
- package/dist/ui/managed-agents-console/src/auth.jsx +215 -0
- package/dist/ui/managed-agents-console/src/console.css +733 -0
- package/dist/ui/managed-agents-console/src/data.js +188 -0
- package/dist/ui/managed-agents-console/src/detail.jsx +719 -0
- package/dist/ui/managed-agents-console/src/docs.jsx +200 -0
- package/dist/ui/managed-agents-console/src/environments.jsx +365 -0
- package/dist/ui/managed-agents-console/src/forms.jsx +494 -0
- package/dist/ui/managed-agents-console/src/icons.jsx +48 -0
- package/dist/ui/managed-agents-console/src/skills.jsx +21 -0
- package/dist/ui/managed-agents-console/src/sse.js +185 -0
- package/dist/ui/managed-agents-console/src/states.jsx +90 -0
- package/dist/ui/managed-agents-console/src/tweaks-panel.jsx +541 -0
- package/dist/ui/managed-agents-console/src/ui.jsx +190 -0
- package/dist/ui/managed-agents-console/src/vaults-data.js +89 -0
- package/dist/ui/managed-agents-console/src/vaults.jsx +171 -0
- package/dist/ui/managed-agents-console/vendor/babel.min.js +4 -0
- package/dist/ui/managed-agents-console/vendor/react-dom.production.min.js +267 -0
- package/dist/ui/managed-agents-console/vendor/react.production.min.js +31 -0
- package/dist/ui/openapi-docs/VENDOR.md +27 -0
- package/dist/ui/openapi-docs/index.html +16 -0
- package/dist/ui/openapi-docs/swagger-initializer.js +14 -0
- package/dist/ui/openapi-docs/vendor/LICENSE +202 -0
- package/dist/ui/openapi-docs/vendor/NOTICE +2 -0
- package/dist/ui/openapi-docs/vendor/swagger-ui-bundle.js +2 -0
- package/dist/ui/openapi-docs/vendor/swagger-ui-standalone-preset.js +2 -0
- package/dist/ui/openapi-docs/vendor/swagger-ui.css +3 -0
- package/package.json +53 -0
|
@@ -0,0 +1,538 @@
|
|
|
1
|
+
import { createHmac, randomBytes, timingSafeEqual } from "node:crypto";
|
|
2
|
+
import { DatabaseSync } from "node:sqlite";
|
|
3
|
+
import { invalidRequest } from "../errors.js";
|
|
4
|
+
import { withSqliteTransaction } from "../sqlite-transaction.js";
|
|
5
|
+
const SCHEMA = `
|
|
6
|
+
CREATE TABLE IF NOT EXISTS sessions (
|
|
7
|
+
id TEXT PRIMARY KEY,
|
|
8
|
+
workspace_id TEXT NOT NULL,
|
|
9
|
+
type TEXT NOT NULL,
|
|
10
|
+
agent_id TEXT NOT NULL,
|
|
11
|
+
agent_version INTEGER NOT NULL,
|
|
12
|
+
environment_id TEXT NOT NULL,
|
|
13
|
+
vault_ids TEXT NOT NULL DEFAULT '[]',
|
|
14
|
+
status TEXT NOT NULL,
|
|
15
|
+
title TEXT,
|
|
16
|
+
metadata TEXT NOT NULL,
|
|
17
|
+
created_at TEXT NOT NULL,
|
|
18
|
+
updated_at TEXT NOT NULL,
|
|
19
|
+
archived_at TEXT,
|
|
20
|
+
usage TEXT
|
|
21
|
+
);
|
|
22
|
+
CREATE INDEX IF NOT EXISTS sessions_by_workspace ON sessions (workspace_id, id);
|
|
23
|
+
CREATE INDEX IF NOT EXISTS sessions_by_workspace_agent ON sessions (workspace_id, agent_id, id);
|
|
24
|
+
-- Partial index so the /metrics active-sessions gauge scans O(active), not
|
|
25
|
+
-- O(history) (plan 0121 §3.2).
|
|
26
|
+
CREATE INDEX IF NOT EXISTS idx_sessions_live ON sessions (archived_at) WHERE archived_at IS NULL;
|
|
27
|
+
|
|
28
|
+
CREATE TABLE IF NOT EXISTS session_resources (
|
|
29
|
+
id TEXT PRIMARY KEY,
|
|
30
|
+
workspace_id TEXT NOT NULL,
|
|
31
|
+
session_id TEXT NOT NULL,
|
|
32
|
+
type TEXT NOT NULL,
|
|
33
|
+
file_id TEXT NOT NULL,
|
|
34
|
+
mount_path TEXT NOT NULL,
|
|
35
|
+
position INTEGER NOT NULL,
|
|
36
|
+
created_at TEXT NOT NULL,
|
|
37
|
+
updated_at TEXT NOT NULL
|
|
38
|
+
);
|
|
39
|
+
CREATE INDEX IF NOT EXISTS session_resources_by_session
|
|
40
|
+
ON session_resources (workspace_id, session_id, id);
|
|
41
|
+
|
|
42
|
+
CREATE TABLE IF NOT EXISTS session_file_mount_snapshots (
|
|
43
|
+
workspace_id TEXT NOT NULL,
|
|
44
|
+
session_id TEXT NOT NULL,
|
|
45
|
+
resource_id TEXT NOT NULL,
|
|
46
|
+
file_id TEXT NOT NULL,
|
|
47
|
+
mount_path TEXT NOT NULL,
|
|
48
|
+
snapshot_file_id TEXT NOT NULL,
|
|
49
|
+
sha256 TEXT NOT NULL,
|
|
50
|
+
size_bytes INTEGER NOT NULL,
|
|
51
|
+
kind TEXT NOT NULL DEFAULT 'upload',
|
|
52
|
+
skill_snapshot_id TEXT,
|
|
53
|
+
PRIMARY KEY (workspace_id, session_id, resource_id)
|
|
54
|
+
);
|
|
55
|
+
CREATE TABLE IF NOT EXISTS session_skill_snapshots (
|
|
56
|
+
workspace_id TEXT NOT NULL,
|
|
57
|
+
session_id TEXT NOT NULL,
|
|
58
|
+
skill_snapshot_id TEXT NOT NULL,
|
|
59
|
+
skill_id TEXT NOT NULL,
|
|
60
|
+
version TEXT NOT NULL,
|
|
61
|
+
name TEXT NOT NULL,
|
|
62
|
+
description TEXT NOT NULL,
|
|
63
|
+
PRIMARY KEY (workspace_id, session_id, skill_snapshot_id)
|
|
64
|
+
);
|
|
65
|
+
CREATE INDEX IF NOT EXISTS session_file_mount_snapshots_by_session
|
|
66
|
+
ON session_file_mount_snapshots (workspace_id, session_id);
|
|
67
|
+
|
|
68
|
+
CREATE TABLE IF NOT EXISTS pending_internal_snapshot_deletes (
|
|
69
|
+
workspace_id TEXT NOT NULL,
|
|
70
|
+
session_id TEXT NOT NULL,
|
|
71
|
+
resource_id TEXT NOT NULL,
|
|
72
|
+
file_id TEXT NOT NULL,
|
|
73
|
+
mount_path TEXT NOT NULL,
|
|
74
|
+
snapshot_file_id TEXT NOT NULL,
|
|
75
|
+
sha256 TEXT NOT NULL,
|
|
76
|
+
size_bytes INTEGER NOT NULL,
|
|
77
|
+
kind TEXT NOT NULL DEFAULT 'upload',
|
|
78
|
+
skill_snapshot_id TEXT,
|
|
79
|
+
created_at TEXT NOT NULL,
|
|
80
|
+
last_attempt_at TEXT,
|
|
81
|
+
attempt_count INTEGER NOT NULL DEFAULT 0,
|
|
82
|
+
last_error TEXT,
|
|
83
|
+
PRIMARY KEY (workspace_id, session_id, resource_id)
|
|
84
|
+
);
|
|
85
|
+
|
|
86
|
+
CREATE TABLE IF NOT EXISTS pending_internal_snapshot_create_rollbacks (
|
|
87
|
+
workspace_id TEXT NOT NULL,
|
|
88
|
+
session_id TEXT NOT NULL,
|
|
89
|
+
resource_id TEXT NOT NULL,
|
|
90
|
+
file_id TEXT NOT NULL,
|
|
91
|
+
mount_path TEXT NOT NULL,
|
|
92
|
+
snapshot_file_id TEXT NOT NULL,
|
|
93
|
+
sha256 TEXT NOT NULL,
|
|
94
|
+
size_bytes INTEGER NOT NULL,
|
|
95
|
+
kind TEXT NOT NULL DEFAULT 'upload',
|
|
96
|
+
skill_snapshot_id TEXT,
|
|
97
|
+
created_at TEXT NOT NULL,
|
|
98
|
+
last_attempt_at TEXT,
|
|
99
|
+
attempt_count INTEGER NOT NULL DEFAULT 0,
|
|
100
|
+
last_error TEXT,
|
|
101
|
+
PRIMARY KEY (workspace_id, session_id, resource_id)
|
|
102
|
+
);
|
|
103
|
+
`;
|
|
104
|
+
export class SqliteSessionStore {
|
|
105
|
+
db;
|
|
106
|
+
cursorSigningKey = randomBytes(32);
|
|
107
|
+
insertStmt;
|
|
108
|
+
insertResourceStmt;
|
|
109
|
+
insertSnapshotStmt;
|
|
110
|
+
insertSkillSnapshotStmt;
|
|
111
|
+
retrieveActiveStmt;
|
|
112
|
+
countActiveStmt;
|
|
113
|
+
hasEnvironmentReferenceStmt;
|
|
114
|
+
countAllActiveStmt;
|
|
115
|
+
retrieveAnyStmt;
|
|
116
|
+
archiveStmt;
|
|
117
|
+
insertPendingSnapshotDeletesStmt;
|
|
118
|
+
deleteStmt;
|
|
119
|
+
deleteResourcesStmt;
|
|
120
|
+
deleteSnapshotsStmt;
|
|
121
|
+
resourcesBySessionStmt;
|
|
122
|
+
snapshotsBySessionStmt;
|
|
123
|
+
skillSnapshotsBySessionStmt;
|
|
124
|
+
pendingSnapshotDeleteWorkspacesStmt;
|
|
125
|
+
pendingSnapshotDeletesByWorkspaceStmt;
|
|
126
|
+
pendingSnapshotDeletesBySessionStmt;
|
|
127
|
+
recordPendingSnapshotDeleteAttemptStmt;
|
|
128
|
+
clearPendingSnapshotDeleteStmt;
|
|
129
|
+
insertPendingSnapshotCreateRollbackStmt;
|
|
130
|
+
clearPendingSnapshotCreateRollbacksBySessionStmt;
|
|
131
|
+
pendingSnapshotCreateRollbackWorkspacesStmt;
|
|
132
|
+
pendingSnapshotCreateRollbacksByWorkspaceStmt;
|
|
133
|
+
pendingSnapshotCreateRollbacksBySessionStmt;
|
|
134
|
+
recordPendingSnapshotCreateRollbackAttemptStmt;
|
|
135
|
+
clearPendingSnapshotCreateRollbackStmt;
|
|
136
|
+
listStmts = new Map();
|
|
137
|
+
constructor(db) {
|
|
138
|
+
this.db = db;
|
|
139
|
+
this.db.exec(SCHEMA);
|
|
140
|
+
ensureVaultIdsColumn(this.db);
|
|
141
|
+
ensureSnapshotColumns(this.db);
|
|
142
|
+
this.insertStmt = this.db.prepare(`INSERT INTO sessions (
|
|
143
|
+
id, workspace_id, type, agent_id, agent_version, environment_id,
|
|
144
|
+
vault_ids, status, title, metadata, created_at, updated_at, archived_at, usage
|
|
145
|
+
) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)`);
|
|
146
|
+
this.insertResourceStmt = this.db.prepare(`INSERT INTO session_resources (
|
|
147
|
+
id, workspace_id, session_id, type, file_id, mount_path, position, created_at, updated_at
|
|
148
|
+
) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?)`);
|
|
149
|
+
this.insertSnapshotStmt = this.db.prepare(`INSERT INTO session_file_mount_snapshots (
|
|
150
|
+
workspace_id, session_id, resource_id, file_id, mount_path,
|
|
151
|
+
snapshot_file_id, sha256, size_bytes, kind, skill_snapshot_id
|
|
152
|
+
) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?)`);
|
|
153
|
+
this.insertSkillSnapshotStmt = this.db.prepare(`INSERT INTO session_skill_snapshots
|
|
154
|
+
(workspace_id,session_id,skill_snapshot_id,skill_id,version,name,description)
|
|
155
|
+
VALUES (?,?,?,?,?,?,?)`);
|
|
156
|
+
this.retrieveActiveStmt = this.db.prepare(`SELECT * FROM sessions
|
|
157
|
+
WHERE workspace_id = ? AND id = ? AND archived_at IS NULL`);
|
|
158
|
+
this.countActiveStmt = this.db.prepare(`SELECT COUNT(*) AS n FROM sessions
|
|
159
|
+
WHERE workspace_id = ? AND archived_at IS NULL`);
|
|
160
|
+
this.hasEnvironmentReferenceStmt = this.db.prepare(`SELECT EXISTS(
|
|
161
|
+
SELECT 1 FROM sessions WHERE workspace_id = ? AND environment_id = ?
|
|
162
|
+
) AS exists_value`);
|
|
163
|
+
this.countAllActiveStmt = this.db.prepare(`SELECT COUNT(*) AS n FROM sessions WHERE archived_at IS NULL`);
|
|
164
|
+
this.retrieveAnyStmt = this.db.prepare(`SELECT * FROM sessions
|
|
165
|
+
WHERE workspace_id = ? AND id = ?`);
|
|
166
|
+
this.archiveStmt = this.db.prepare(`UPDATE sessions
|
|
167
|
+
SET status = 'terminated', updated_at = ?, archived_at = COALESCE(archived_at, ?)
|
|
168
|
+
WHERE workspace_id = ? AND id = ?`);
|
|
169
|
+
this.insertPendingSnapshotDeletesStmt = this.db.prepare(`INSERT OR IGNORE INTO pending_internal_snapshot_deletes (
|
|
170
|
+
workspace_id, session_id, resource_id, file_id, mount_path,
|
|
171
|
+
snapshot_file_id, sha256, size_bytes, kind, skill_snapshot_id, created_at
|
|
172
|
+
)
|
|
173
|
+
SELECT
|
|
174
|
+
workspace_id, session_id, resource_id, file_id, mount_path,
|
|
175
|
+
snapshot_file_id, sha256, size_bytes, kind, skill_snapshot_id, ?
|
|
176
|
+
FROM session_file_mount_snapshots
|
|
177
|
+
WHERE workspace_id = ? AND session_id = ?`);
|
|
178
|
+
this.deleteStmt = this.db.prepare(`DELETE FROM sessions
|
|
179
|
+
WHERE workspace_id = ? AND id = ?`);
|
|
180
|
+
this.deleteResourcesStmt = this.db.prepare(`DELETE FROM session_resources
|
|
181
|
+
WHERE workspace_id = ? AND session_id = ?`);
|
|
182
|
+
this.deleteSnapshotsStmt = this.db.prepare(`DELETE FROM session_file_mount_snapshots
|
|
183
|
+
WHERE workspace_id = ? AND session_id = ?`);
|
|
184
|
+
this.resourcesBySessionStmt = this.db.prepare(`SELECT * FROM session_resources
|
|
185
|
+
WHERE workspace_id = ? AND session_id = ?
|
|
186
|
+
ORDER BY position ASC, id ASC`);
|
|
187
|
+
this.snapshotsBySessionStmt = this.db.prepare(`SELECT * FROM session_file_mount_snapshots
|
|
188
|
+
WHERE workspace_id = ? AND session_id = ?
|
|
189
|
+
ORDER BY resource_id ASC`);
|
|
190
|
+
this.skillSnapshotsBySessionStmt = this.db.prepare(`SELECT * FROM session_skill_snapshots WHERE workspace_id=? AND session_id=? ORDER BY skill_snapshot_id`);
|
|
191
|
+
this.pendingSnapshotDeleteWorkspacesStmt = this.db.prepare(`SELECT DISTINCT workspace_id
|
|
192
|
+
FROM pending_internal_snapshot_deletes
|
|
193
|
+
ORDER BY workspace_id ASC`);
|
|
194
|
+
this.pendingSnapshotDeletesByWorkspaceStmt = this.db.prepare(`SELECT *
|
|
195
|
+
FROM pending_internal_snapshot_deletes
|
|
196
|
+
WHERE workspace_id = ?
|
|
197
|
+
ORDER BY session_id ASC, resource_id ASC`);
|
|
198
|
+
this.pendingSnapshotDeletesBySessionStmt = this.db.prepare(`SELECT *
|
|
199
|
+
FROM pending_internal_snapshot_deletes
|
|
200
|
+
WHERE workspace_id = ? AND session_id = ?
|
|
201
|
+
ORDER BY resource_id ASC`);
|
|
202
|
+
this.recordPendingSnapshotDeleteAttemptStmt = this.db.prepare(`UPDATE pending_internal_snapshot_deletes
|
|
203
|
+
SET last_attempt_at = ?, attempt_count = attempt_count + 1, last_error = ?
|
|
204
|
+
WHERE workspace_id = ? AND session_id = ? AND resource_id = ?`);
|
|
205
|
+
this.clearPendingSnapshotDeleteStmt = this.db.prepare(`DELETE FROM pending_internal_snapshot_deletes
|
|
206
|
+
WHERE workspace_id = ? AND session_id = ? AND resource_id = ?`);
|
|
207
|
+
this.insertPendingSnapshotCreateRollbackStmt = this.db.prepare(`INSERT INTO pending_internal_snapshot_create_rollbacks (
|
|
208
|
+
workspace_id, session_id, resource_id, file_id, mount_path,
|
|
209
|
+
snapshot_file_id, sha256, size_bytes, kind, skill_snapshot_id, created_at
|
|
210
|
+
) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)`);
|
|
211
|
+
this.clearPendingSnapshotCreateRollbacksBySessionStmt = this.db.prepare(`DELETE FROM pending_internal_snapshot_create_rollbacks
|
|
212
|
+
WHERE workspace_id = ? AND session_id = ?`);
|
|
213
|
+
this.pendingSnapshotCreateRollbackWorkspacesStmt = this.db.prepare(`SELECT DISTINCT workspace_id
|
|
214
|
+
FROM pending_internal_snapshot_create_rollbacks
|
|
215
|
+
ORDER BY workspace_id ASC`);
|
|
216
|
+
this.pendingSnapshotCreateRollbacksByWorkspaceStmt = this.db.prepare(`SELECT *
|
|
217
|
+
FROM pending_internal_snapshot_create_rollbacks
|
|
218
|
+
WHERE workspace_id = ?
|
|
219
|
+
ORDER BY session_id ASC, resource_id ASC`);
|
|
220
|
+
this.pendingSnapshotCreateRollbacksBySessionStmt = this.db.prepare(`SELECT *
|
|
221
|
+
FROM pending_internal_snapshot_create_rollbacks
|
|
222
|
+
WHERE workspace_id = ? AND session_id = ?
|
|
223
|
+
ORDER BY resource_id ASC`);
|
|
224
|
+
this.recordPendingSnapshotCreateRollbackAttemptStmt = this.db.prepare(`UPDATE pending_internal_snapshot_create_rollbacks
|
|
225
|
+
SET last_attempt_at = ?, attempt_count = attempt_count + 1, last_error = ?
|
|
226
|
+
WHERE workspace_id = ? AND session_id = ? AND resource_id = ?`);
|
|
227
|
+
this.clearPendingSnapshotCreateRollbackStmt = this.db.prepare(`DELETE FROM pending_internal_snapshot_create_rollbacks
|
|
228
|
+
WHERE workspace_id = ? AND session_id = ? AND resource_id = ?`);
|
|
229
|
+
}
|
|
230
|
+
static open(path = ":memory:") {
|
|
231
|
+
return new SqliteSessionStore(new DatabaseSync(path));
|
|
232
|
+
}
|
|
233
|
+
create(record) {
|
|
234
|
+
return this.createInTransaction(record);
|
|
235
|
+
}
|
|
236
|
+
createAndCompleteIdempotency(record, idempotency) {
|
|
237
|
+
return this.createInTransaction(record, idempotency);
|
|
238
|
+
}
|
|
239
|
+
createInTransaction(record, idempotency) {
|
|
240
|
+
const s = record.row;
|
|
241
|
+
return this.withTransaction(() => {
|
|
242
|
+
this.insertStmt.run(s.id, s.workspace_id, s.type, s.agent.id, s.agent.version, s.environment_id, JSON.stringify(s.vault_ids ?? []), s.status, s.title, JSON.stringify(s.metadata), s.created_at, s.updated_at, s.archived_at, s.usage);
|
|
243
|
+
for (const [position, resource] of s.resources.entries()) {
|
|
244
|
+
this.insertResourceStmt.run(resource.id, s.workspace_id, s.id, resource.type, resource.file_id, resource.mount_path, position, resource.created_at, resource.updated_at);
|
|
245
|
+
}
|
|
246
|
+
for (const snapshot of record.snapshots ?? []) {
|
|
247
|
+
this.insertSnapshotStmt.run(snapshot.workspace_id, snapshot.session_id, snapshot.resource_id, snapshot.file_id, snapshot.mount_path, snapshot.snapshot_file_id, snapshot.sha256, snapshot.size_bytes, snapshot.kind, snapshot.skill_snapshot_id);
|
|
248
|
+
}
|
|
249
|
+
for (const skill of record.skillSnapshots ?? []) {
|
|
250
|
+
this.insertSkillSnapshotStmt.run(skill.workspace_id, skill.session_id, skill.skill_snapshot_id, skill.skill_id, skill.version, skill.name, skill.description);
|
|
251
|
+
}
|
|
252
|
+
idempotency?.complete();
|
|
253
|
+
this.clearPendingSnapshotCreateRollbacksBySessionStmt.run(s.workspace_id, s.id);
|
|
254
|
+
return s;
|
|
255
|
+
});
|
|
256
|
+
}
|
|
257
|
+
retrieve(workspaceId, sessionId) {
|
|
258
|
+
const row = this.retrieveActiveStmt.get(workspaceId, sessionId);
|
|
259
|
+
return row ? this.deserialize(row) : undefined;
|
|
260
|
+
}
|
|
261
|
+
countActive(workspaceId) {
|
|
262
|
+
return this.countActiveStmt.get(workspaceId).n;
|
|
263
|
+
}
|
|
264
|
+
hasEnvironmentReference(workspaceId, environmentId) {
|
|
265
|
+
return this.hasEnvironmentReferenceStmt.get(workspaceId, environmentId).exists_value === 1;
|
|
266
|
+
}
|
|
267
|
+
// Unscoped, for the /metrics gauge (0121 C2) — served by idx_sessions_live.
|
|
268
|
+
countAllActive() {
|
|
269
|
+
return this.countAllActiveStmt.get().n;
|
|
270
|
+
}
|
|
271
|
+
retrieveAny(workspaceId, sessionId) {
|
|
272
|
+
const row = this.retrieveAnyStmt.get(workspaceId, sessionId);
|
|
273
|
+
return row ? this.deserialize(row) : undefined;
|
|
274
|
+
}
|
|
275
|
+
archive(workspaceId, sessionId, archivedAt) {
|
|
276
|
+
this.archiveStmt.run(archivedAt, archivedAt, workspaceId, sessionId);
|
|
277
|
+
return this.retrieveAny(workspaceId, sessionId);
|
|
278
|
+
}
|
|
279
|
+
delete(workspaceId, sessionId) {
|
|
280
|
+
const existing = this.retrieveAny(workspaceId, sessionId);
|
|
281
|
+
if (!existing)
|
|
282
|
+
return undefined;
|
|
283
|
+
const now = new Date().toISOString();
|
|
284
|
+
return this.withTransaction(() => {
|
|
285
|
+
this.insertPendingSnapshotDeletesStmt.run(now, workspaceId, sessionId);
|
|
286
|
+
this.deleteSnapshotsStmt.run(workspaceId, sessionId);
|
|
287
|
+
this.db.prepare("DELETE FROM session_skill_snapshots WHERE workspace_id=? AND session_id=?").run(workspaceId, sessionId);
|
|
288
|
+
this.deleteResourcesStmt.run(workspaceId, sessionId);
|
|
289
|
+
this.deleteStmt.run(workspaceId, sessionId);
|
|
290
|
+
return existing;
|
|
291
|
+
});
|
|
292
|
+
}
|
|
293
|
+
withTransaction(fn) {
|
|
294
|
+
return withSqliteTransaction(this.db, fn);
|
|
295
|
+
}
|
|
296
|
+
getFileMountSnapshots(workspaceId, sessionId) {
|
|
297
|
+
return this.snapshotsBySessionStmt.all(workspaceId, sessionId);
|
|
298
|
+
}
|
|
299
|
+
getSkillSnapshots(workspaceId, sessionId) {
|
|
300
|
+
return this.skillSnapshotsBySessionStmt.all(workspaceId, sessionId);
|
|
301
|
+
}
|
|
302
|
+
listPendingInternalSnapshotDeleteWorkspaces() {
|
|
303
|
+
const rows = this.pendingSnapshotDeleteWorkspacesStmt.all();
|
|
304
|
+
return rows.map((row) => row.workspace_id);
|
|
305
|
+
}
|
|
306
|
+
getPendingInternalSnapshotDeletes(workspaceId, sessionId) {
|
|
307
|
+
const rows = sessionId === undefined
|
|
308
|
+
? this.pendingSnapshotDeletesByWorkspaceStmt.all(workspaceId)
|
|
309
|
+
: this.pendingSnapshotDeletesBySessionStmt.all(workspaceId, sessionId);
|
|
310
|
+
return rows;
|
|
311
|
+
}
|
|
312
|
+
recordPendingInternalSnapshotDeleteAttempt(workspaceId, sessionId, resourceId, attemptedAt, error) {
|
|
313
|
+
this.recordPendingSnapshotDeleteAttemptStmt.run(attemptedAt, error, workspaceId, sessionId, resourceId);
|
|
314
|
+
}
|
|
315
|
+
clearPendingInternalSnapshotDelete(workspaceId, sessionId, resourceId) {
|
|
316
|
+
this.clearPendingSnapshotDeleteStmt.run(workspaceId, sessionId, resourceId);
|
|
317
|
+
}
|
|
318
|
+
recordPendingInternalSnapshotCreateRollback(row, createdAt) {
|
|
319
|
+
this.insertPendingSnapshotCreateRollbackStmt.run(row.workspace_id, row.session_id, row.resource_id, row.file_id, row.mount_path, row.snapshot_file_id, row.sha256, row.size_bytes, row.kind, row.skill_snapshot_id, createdAt);
|
|
320
|
+
}
|
|
321
|
+
listPendingInternalSnapshotCreateRollbackWorkspaces() {
|
|
322
|
+
const rows = this.pendingSnapshotCreateRollbackWorkspacesStmt.all();
|
|
323
|
+
return rows.map((row) => row.workspace_id);
|
|
324
|
+
}
|
|
325
|
+
getPendingInternalSnapshotCreateRollbacks(workspaceId, sessionId) {
|
|
326
|
+
const rows = sessionId === undefined
|
|
327
|
+
? this.pendingSnapshotCreateRollbacksByWorkspaceStmt.all(workspaceId)
|
|
328
|
+
: this.pendingSnapshotCreateRollbacksBySessionStmt.all(workspaceId, sessionId);
|
|
329
|
+
return rows;
|
|
330
|
+
}
|
|
331
|
+
recordPendingInternalSnapshotCreateRollbackAttempt(workspaceId, sessionId, resourceId, attemptedAt, error) {
|
|
332
|
+
this.recordPendingSnapshotCreateRollbackAttemptStmt.run(attemptedAt, error, workspaceId, sessionId, resourceId);
|
|
333
|
+
}
|
|
334
|
+
clearPendingInternalSnapshotCreateRollback(workspaceId, sessionId, resourceId) {
|
|
335
|
+
this.clearPendingSnapshotCreateRollbackStmt.run(workspaceId, sessionId, resourceId);
|
|
336
|
+
}
|
|
337
|
+
list(workspaceId, opts = {}) {
|
|
338
|
+
if (opts.page === "") {
|
|
339
|
+
return { data: [], next_page: null, prev_page: null };
|
|
340
|
+
}
|
|
341
|
+
const limit = normalizeLimit(opts.limit);
|
|
342
|
+
const order = opts.order ?? "desc";
|
|
343
|
+
const includeArchived = opts.includeArchived ?? false;
|
|
344
|
+
const cursor = opts.page === undefined
|
|
345
|
+
? undefined
|
|
346
|
+
: decodeSessionCursor(opts.page, { order, agentId: opts.agentId, includeArchived }, workspaceId, this.cursorSigningKey);
|
|
347
|
+
const direction = cursor?.direction ?? "next";
|
|
348
|
+
const queryOrder = direction === "prev"
|
|
349
|
+
? (order === "asc" ? "desc" : "asc")
|
|
350
|
+
: order;
|
|
351
|
+
const stmt = this.listStmt({
|
|
352
|
+
includeArchived,
|
|
353
|
+
hasAgent: opts.agentId !== undefined,
|
|
354
|
+
hasPage: cursor !== undefined,
|
|
355
|
+
order: queryOrder,
|
|
356
|
+
});
|
|
357
|
+
const rows = stmt.all(...selectListArgs(workspaceId, limit + 1, opts.agentId, cursor?.anchor));
|
|
358
|
+
const hasMoreInQueryDirection = rows.length > limit;
|
|
359
|
+
const selected = rows.slice(0, limit);
|
|
360
|
+
if (direction === "prev")
|
|
361
|
+
selected.reverse();
|
|
362
|
+
const data = selected.map((row) => this.deserialize(row));
|
|
363
|
+
const hasNext = direction === "prev" ? cursor !== undefined : hasMoreInQueryDirection;
|
|
364
|
+
const hasPrev = direction === "prev" ? hasMoreInQueryDirection : cursor !== undefined;
|
|
365
|
+
const context = { order, agentId: opts.agentId, includeArchived };
|
|
366
|
+
return {
|
|
367
|
+
data,
|
|
368
|
+
next_page: hasNext && data.length > 0
|
|
369
|
+
? encodeSessionCursor(data[data.length - 1].id, "next", context, workspaceId, this.cursorSigningKey)
|
|
370
|
+
: null,
|
|
371
|
+
prev_page: hasPrev && data.length > 0
|
|
372
|
+
? encodeSessionCursor(data[0].id, "prev", context, workspaceId, this.cursorSigningKey)
|
|
373
|
+
: null,
|
|
374
|
+
};
|
|
375
|
+
}
|
|
376
|
+
close() {
|
|
377
|
+
this.db.close();
|
|
378
|
+
}
|
|
379
|
+
listStmt(opts) {
|
|
380
|
+
const key = JSON.stringify(opts);
|
|
381
|
+
const existing = this.listStmts.get(key);
|
|
382
|
+
if (existing)
|
|
383
|
+
return existing;
|
|
384
|
+
const predicates = ["workspace_id = ?"];
|
|
385
|
+
if (opts.hasAgent)
|
|
386
|
+
predicates.push("agent_id = ?");
|
|
387
|
+
if (!opts.includeArchived)
|
|
388
|
+
predicates.push("archived_at IS NULL");
|
|
389
|
+
if (opts.hasPage)
|
|
390
|
+
predicates.push(opts.order === "asc" ? "id > ?" : "id < ?");
|
|
391
|
+
const stmt = this.db.prepare(`SELECT * FROM sessions
|
|
392
|
+
WHERE ${predicates.join(" AND ")}
|
|
393
|
+
ORDER BY id ${opts.order.toUpperCase()}
|
|
394
|
+
LIMIT ?`);
|
|
395
|
+
this.listStmts.set(key, stmt);
|
|
396
|
+
return stmt;
|
|
397
|
+
}
|
|
398
|
+
deserialize(row) {
|
|
399
|
+
return {
|
|
400
|
+
id: row.id,
|
|
401
|
+
workspace_id: row.workspace_id,
|
|
402
|
+
type: "session",
|
|
403
|
+
agent: {
|
|
404
|
+
type: "agent",
|
|
405
|
+
id: row.agent_id,
|
|
406
|
+
version: row.agent_version,
|
|
407
|
+
},
|
|
408
|
+
environment_id: row.environment_id,
|
|
409
|
+
vault_ids: parseVaultIds(row.vault_ids),
|
|
410
|
+
status: row.status,
|
|
411
|
+
title: row.title,
|
|
412
|
+
metadata: JSON.parse(row.metadata),
|
|
413
|
+
created_at: row.created_at,
|
|
414
|
+
updated_at: row.updated_at,
|
|
415
|
+
archived_at: row.archived_at,
|
|
416
|
+
// B.1 has no runtime usage source yet; Cycle C/D should deserialize this column.
|
|
417
|
+
usage: null,
|
|
418
|
+
resources: this.resourcesBySessionStmt.all(row.workspace_id, row.id),
|
|
419
|
+
};
|
|
420
|
+
}
|
|
421
|
+
}
|
|
422
|
+
function ensureVaultIdsColumn(db) {
|
|
423
|
+
const columns = db.prepare("PRAGMA table_info(sessions)").all();
|
|
424
|
+
if (columns.some((column) => column.name === "vault_ids"))
|
|
425
|
+
return;
|
|
426
|
+
db.exec("ALTER TABLE sessions ADD COLUMN vault_ids TEXT NOT NULL DEFAULT '[]'");
|
|
427
|
+
}
|
|
428
|
+
function ensureSnapshotColumns(db) {
|
|
429
|
+
for (const table of [
|
|
430
|
+
"session_file_mount_snapshots",
|
|
431
|
+
"pending_internal_snapshot_deletes",
|
|
432
|
+
"pending_internal_snapshot_create_rollbacks",
|
|
433
|
+
]) {
|
|
434
|
+
const columns = db.prepare(`PRAGMA table_info(${table})`).all();
|
|
435
|
+
if (!columns.some((column) => column.name === "kind")) {
|
|
436
|
+
db.exec(`ALTER TABLE ${table} ADD COLUMN kind TEXT NOT NULL DEFAULT 'upload'`);
|
|
437
|
+
}
|
|
438
|
+
if (!columns.some((column) => column.name === "skill_snapshot_id")) {
|
|
439
|
+
db.exec(`ALTER TABLE ${table} ADD COLUMN skill_snapshot_id TEXT`);
|
|
440
|
+
}
|
|
441
|
+
}
|
|
442
|
+
}
|
|
443
|
+
function parseVaultIds(value) {
|
|
444
|
+
const parsed = JSON.parse(value);
|
|
445
|
+
return Array.isArray(parsed) && parsed.every((item) => typeof item === "string")
|
|
446
|
+
? [...parsed]
|
|
447
|
+
: [];
|
|
448
|
+
}
|
|
449
|
+
function encodeSessionCursor(anchor, direction, context, workspaceId, signingKey) {
|
|
450
|
+
const payload = {
|
|
451
|
+
v: 1,
|
|
452
|
+
anchor,
|
|
453
|
+
direction,
|
|
454
|
+
order: context.order,
|
|
455
|
+
agentId: context.agentId ?? null,
|
|
456
|
+
includeArchived: context.includeArchived,
|
|
457
|
+
};
|
|
458
|
+
const encodedPayload = Buffer.from(JSON.stringify(payload), "utf8").toString("base64url");
|
|
459
|
+
const signature = signSessionCursor(encodedPayload, workspaceId, signingKey);
|
|
460
|
+
return `${encodedPayload}.${signature.toString("base64url")}`;
|
|
461
|
+
}
|
|
462
|
+
function decodeSessionCursor(value, context, workspaceId, signingKey) {
|
|
463
|
+
let payload;
|
|
464
|
+
try {
|
|
465
|
+
const parts = value.split(".");
|
|
466
|
+
if (parts.length !== 2)
|
|
467
|
+
throw new Error("invalid signed cursor");
|
|
468
|
+
const [encodedPayload, encodedSignature] = parts;
|
|
469
|
+
const decoded = decodeCanonicalBase64url(encodedPayload);
|
|
470
|
+
const signature = decodeCanonicalBase64url(encodedSignature);
|
|
471
|
+
const expected = signSessionCursor(encodedPayload, workspaceId, signingKey);
|
|
472
|
+
if (signature.length !== expected.length ||
|
|
473
|
+
!timingSafeEqual(signature, expected)) {
|
|
474
|
+
throw new Error("invalid cursor signature");
|
|
475
|
+
}
|
|
476
|
+
payload = JSON.parse(decoded.toString("utf8"));
|
|
477
|
+
}
|
|
478
|
+
catch {
|
|
479
|
+
throw invalidRequest("invalid page cursor");
|
|
480
|
+
}
|
|
481
|
+
if (!isSessionCursorPayload(payload))
|
|
482
|
+
throw invalidRequest("invalid page cursor");
|
|
483
|
+
if (payload.order !== context.order) {
|
|
484
|
+
throw invalidRequest("page token order does not match request");
|
|
485
|
+
}
|
|
486
|
+
if (payload.agentId !== (context.agentId ?? null) ||
|
|
487
|
+
payload.includeArchived !== context.includeArchived) {
|
|
488
|
+
throw invalidRequest("page token filters do not match request");
|
|
489
|
+
}
|
|
490
|
+
return payload;
|
|
491
|
+
}
|
|
492
|
+
function signSessionCursor(encodedPayload, workspaceId, signingKey) {
|
|
493
|
+
return createHmac("sha256", signingKey)
|
|
494
|
+
.update("oma-session-page-v1\0", "utf8")
|
|
495
|
+
.update(workspaceId, "utf8")
|
|
496
|
+
.update("\0", "utf8")
|
|
497
|
+
.update(encodedPayload, "utf8")
|
|
498
|
+
.digest();
|
|
499
|
+
}
|
|
500
|
+
function decodeCanonicalBase64url(value) {
|
|
501
|
+
if (!/^[A-Za-z0-9_-]+$/.test(value)) {
|
|
502
|
+
throw new Error("non-canonical base64url");
|
|
503
|
+
}
|
|
504
|
+
const decoded = Buffer.from(value, "base64url");
|
|
505
|
+
if (decoded.toString("base64url") !== value) {
|
|
506
|
+
throw new Error("non-canonical base64url");
|
|
507
|
+
}
|
|
508
|
+
return decoded;
|
|
509
|
+
}
|
|
510
|
+
function isSessionCursorPayload(value) {
|
|
511
|
+
if (typeof value !== "object" || value === null)
|
|
512
|
+
return false;
|
|
513
|
+
const payload = value;
|
|
514
|
+
return payload.v === 1 &&
|
|
515
|
+
typeof payload.anchor === "string" && payload.anchor.length > 0 &&
|
|
516
|
+
(payload.direction === "next" || payload.direction === "prev") &&
|
|
517
|
+
(payload.order === "asc" || payload.order === "desc") &&
|
|
518
|
+
(payload.agentId === null || typeof payload.agentId === "string") &&
|
|
519
|
+
typeof payload.includeArchived === "boolean";
|
|
520
|
+
}
|
|
521
|
+
function selectListArgs(workspaceId, limit, agentId, page) {
|
|
522
|
+
if (agentId === undefined && page === undefined)
|
|
523
|
+
return [workspaceId, limit];
|
|
524
|
+
if (agentId !== undefined && page === undefined) {
|
|
525
|
+
return [workspaceId, agentId, limit];
|
|
526
|
+
}
|
|
527
|
+
if (agentId === undefined && page !== undefined) {
|
|
528
|
+
return [workspaceId, page, limit];
|
|
529
|
+
}
|
|
530
|
+
return [workspaceId, agentId, page, limit];
|
|
531
|
+
}
|
|
532
|
+
function normalizeLimit(limit) {
|
|
533
|
+
if (limit === undefined)
|
|
534
|
+
return 20;
|
|
535
|
+
if (!Number.isSafeInteger(limit) || limit <= 0)
|
|
536
|
+
return 20;
|
|
537
|
+
return Math.min(limit, 100);
|
|
538
|
+
}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export {};
|