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.
Files changed (197) hide show
  1. package/LICENSE +93 -0
  2. package/README.md +248 -0
  3. package/bin/oma.mjs +499 -0
  4. package/dist/scripts/alpha-openai-compatible-fixture.mjs +134 -0
  5. package/dist/scripts/alpha-smoke-models.mjs +92 -0
  6. package/dist/scripts/alpha-smoke.mjs +581 -0
  7. package/dist/scripts/oma-doctor.js +230 -0
  8. package/dist/scripts/oma-models.js +299 -0
  9. package/dist/scripts/oma-workspaces.js +133 -0
  10. package/dist/src/control-plane/admin/auth.js +43 -0
  11. package/dist/src/control-plane/admin/routes.js +76 -0
  12. package/dist/src/control-plane/admin/service.js +102 -0
  13. package/dist/src/control-plane/admission.js +90 -0
  14. package/dist/src/control-plane/agents/routes.js +63 -0
  15. package/dist/src/control-plane/agents/service.js +687 -0
  16. package/dist/src/control-plane/agents/store.js +391 -0
  17. package/dist/src/control-plane/agents/types.js +1 -0
  18. package/dist/src/control-plane/api-constants.js +3 -0
  19. package/dist/src/control-plane/app.js +907 -0
  20. package/dist/src/control-plane/console/auth.js +68 -0
  21. package/dist/src/control-plane/console/static.js +112 -0
  22. package/dist/src/control-plane/deployment-runtime-config.js +328 -0
  23. package/dist/src/control-plane/deployment-runtime-event-coordinator.js +25 -0
  24. package/dist/src/control-plane/deployment-runtime-turn-guard.js +14 -0
  25. package/dist/src/control-plane/deployment-session-coordinator.js +25 -0
  26. package/dist/src/control-plane/deployment-session-output-coordinator.js +27 -0
  27. package/dist/src/control-plane/deployment-storage.js +354 -0
  28. package/dist/src/control-plane/egress/image.js +48 -0
  29. package/dist/src/control-plane/egress/policy.js +571 -0
  30. package/dist/src/control-plane/egress/presets.js +77 -0
  31. package/dist/src/control-plane/egress/proxy.js +72 -0
  32. package/dist/src/control-plane/egress/ssrf.js +106 -0
  33. package/dist/src/control-plane/egress/utils/debug.js +14 -0
  34. package/dist/src/control-plane/egress/vendor/http-proxy.js +335 -0
  35. package/dist/src/control-plane/egress/vendor/mitm-ca.js +226 -0
  36. package/dist/src/control-plane/egress/vendor/mitm-leaf.js +124 -0
  37. package/dist/src/control-plane/egress/vendor/parent-proxy.js +438 -0
  38. package/dist/src/control-plane/egress/vendor/request-filter.js +105 -0
  39. package/dist/src/control-plane/egress/vendor/sandbox-config.js +1 -0
  40. package/dist/src/control-plane/egress/vendor/tls-terminate-proxy.js +269 -0
  41. package/dist/src/control-plane/environments/routes.js +54 -0
  42. package/dist/src/control-plane/environments/service.js +112 -0
  43. package/dist/src/control-plane/environments/store.js +132 -0
  44. package/dist/src/control-plane/environments/types.js +1 -0
  45. package/dist/src/control-plane/errors.js +81 -0
  46. package/dist/src/control-plane/events/broadcaster.js +217 -0
  47. package/dist/src/control-plane/events/constants.js +2 -0
  48. package/dist/src/control-plane/events/persist.js +39 -0
  49. package/dist/src/control-plane/events/request.js +190 -0
  50. package/dist/src/control-plane/events/routes.js +159 -0
  51. package/dist/src/control-plane/events/runtime-helpers.js +134 -0
  52. package/dist/src/control-plane/events/service.js +1985 -0
  53. package/dist/src/control-plane/events/session-guards.js +29 -0
  54. package/dist/src/control-plane/events/sse.js +6 -0
  55. package/dist/src/control-plane/events/store.js +772 -0
  56. package/dist/src/control-plane/events/tool-persistence.js +102 -0
  57. package/dist/src/control-plane/events/types.js +21 -0
  58. package/dist/src/control-plane/files/routes.js +121 -0
  59. package/dist/src/control-plane/files/service.js +65 -0
  60. package/dist/src/control-plane/files/store-common.js +87 -0
  61. package/dist/src/control-plane/files/store-local.js +488 -0
  62. package/dist/src/control-plane/files/store-memory.js +270 -0
  63. package/dist/src/control-plane/files/store.js +2 -0
  64. package/dist/src/control-plane/files/types.js +7 -0
  65. package/dist/src/control-plane/http.js +25 -0
  66. package/dist/src/control-plane/ids.js +43 -0
  67. package/dist/src/control-plane/logging.js +263 -0
  68. package/dist/src/control-plane/models/auth-storage-backend.js +238 -0
  69. package/dist/src/control-plane/models/catalog.js +78 -0
  70. package/dist/src/control-plane/models/config-security.js +376 -0
  71. package/dist/src/control-plane/models/deployment-config.js +93 -0
  72. package/dist/src/control-plane/models/routes.js +32 -0
  73. package/dist/src/control-plane/models/service.js +138 -0
  74. package/dist/src/control-plane/observability/instruments.js +65 -0
  75. package/dist/src/control-plane/observability/metrics.js +125 -0
  76. package/dist/src/control-plane/observability/routes.js +95 -0
  77. package/dist/src/control-plane/openapi/document.js +282 -0
  78. package/dist/src/control-plane/openapi/routes.js +78 -0
  79. package/dist/src/control-plane/request-idempotency.js +85 -0
  80. package/dist/src/control-plane/secrets/envelope.js +132 -0
  81. package/dist/src/control-plane/secrets/master-key.js +44 -0
  82. package/dist/src/control-plane/secrets/routes.js +20 -0
  83. package/dist/src/control-plane/secrets/service.js +71 -0
  84. package/dist/src/control-plane/secrets/store.js +212 -0
  85. package/dist/src/control-plane/secrets/types.js +3 -0
  86. package/dist/src/control-plane/sessions/pi/custom-tools.js +146 -0
  87. package/dist/src/control-plane/sessions/pi/mcp/bridge.js +527 -0
  88. package/dist/src/control-plane/sessions/pi/mcp/client.js +157 -0
  89. package/dist/src/control-plane/sessions/pi/mcp/credential.js +28 -0
  90. package/dist/src/control-plane/sessions/pi/mcp/fetch.js +69 -0
  91. package/dist/src/control-plane/sessions/pi/mcp/probe.js +202 -0
  92. package/dist/src/control-plane/sessions/pi/mcp/runtime.js +13 -0
  93. package/dist/src/control-plane/sessions/pi/runner.js +1104 -0
  94. package/dist/src/control-plane/sessions/pi/sandbox/cma-glob.js +388 -0
  95. package/dist/src/control-plane/sessions/pi/sandbox/cma-grep.js +124 -0
  96. package/dist/src/control-plane/sessions/pi/sandbox/docker-egress.js +347 -0
  97. package/dist/src/control-plane/sessions/pi/sandbox/docker.js +1589 -0
  98. package/dist/src/control-plane/sessions/pi/sandbox/glob.js +48 -0
  99. package/dist/src/control-plane/sessions/pi/sandbox/image.js +7 -0
  100. package/dist/src/control-plane/sessions/pi/sandbox/microsandbox.js +1677 -0
  101. package/dist/src/control-plane/sessions/pi/sandbox/provider.js +561 -0
  102. package/dist/src/control-plane/sessions/pi/sandbox/selection.js +168 -0
  103. package/dist/src/control-plane/sessions/pi/span-normalizer.js +82 -0
  104. package/dist/src/control-plane/sessions/pi/tool-permissions.js +392 -0
  105. package/dist/src/control-plane/sessions/pi/translator.js +100 -0
  106. package/dist/src/control-plane/sessions/request.js +164 -0
  107. package/dist/src/control-plane/sessions/resources.js +63 -0
  108. package/dist/src/control-plane/sessions/routes.js +88 -0
  109. package/dist/src/control-plane/sessions/serialize.js +24 -0
  110. package/dist/src/control-plane/sessions/service.js +733 -0
  111. package/dist/src/control-plane/sessions/store.js +538 -0
  112. package/dist/src/control-plane/sessions/types.js +1 -0
  113. package/dist/src/control-plane/skills/archive.js +169 -0
  114. package/dist/src/control-plane/skills/routes.js +41 -0
  115. package/dist/src/control-plane/skills/service.js +29 -0
  116. package/dist/src/control-plane/skills/store.js +233 -0
  117. package/dist/src/control-plane/skills/types.js +5 -0
  118. package/dist/src/control-plane/sqlite-transaction.js +15 -0
  119. package/dist/src/control-plane/vaults/mcp-oauth-validate.js +165 -0
  120. package/dist/src/control-plane/vaults/oauth-refresh-ticker.js +43 -0
  121. package/dist/src/control-plane/vaults/oauth-refresh.js +448 -0
  122. package/dist/src/control-plane/vaults/routes.js +66 -0
  123. package/dist/src/control-plane/vaults/service.js +553 -0
  124. package/dist/src/control-plane/vaults/store.js +691 -0
  125. package/dist/src/control-plane/vaults/types.js +1 -0
  126. package/dist/src/control-plane/wake-loop.js +65 -0
  127. package/dist/src/control-plane/wiring.js +92 -0
  128. package/dist/src/control-plane/workspace.js +4 -0
  129. package/dist/src/control-plane/workspaces/store.js +158 -0
  130. package/dist/src/egress-proxy-main.js +103 -0
  131. package/dist/src/main.js +124 -0
  132. package/dist/src/types/agents.js +1 -0
  133. package/dist/src/types/common.js +1 -0
  134. package/dist/src/types/environments.js +1 -0
  135. package/dist/src/types/events.js +121 -0
  136. package/dist/src/types/files.js +1 -0
  137. package/dist/src/types/json.js +19 -0
  138. package/dist/src/types/sessions.js +1 -0
  139. package/dist/ui/managed-agents-console/README.md +52 -0
  140. package/dist/ui/managed-agents-console/docs/agents.md +23 -0
  141. package/dist/ui/managed-agents-console/docs/console.md +23 -0
  142. package/dist/ui/managed-agents-console/docs/dreams.md +5 -0
  143. package/dist/ui/managed-agents-console/docs/environments.md +27 -0
  144. package/dist/ui/managed-agents-console/docs/events.md +19 -0
  145. package/dist/ui/managed-agents-console/docs/files.md +17 -0
  146. package/dist/ui/managed-agents-console/docs/github.md +11 -0
  147. package/dist/ui/managed-agents-console/docs/integrations.md +19 -0
  148. package/dist/ui/managed-agents-console/docs/memory.md +11 -0
  149. package/dist/ui/managed-agents-console/docs/migration.md +17 -0
  150. package/dist/ui/managed-agents-console/docs/multiagent.md +11 -0
  151. package/dist/ui/managed-agents-console/docs/outcomes.md +11 -0
  152. package/dist/ui/managed-agents-console/docs/overview.md +41 -0
  153. package/dist/ui/managed-agents-console/docs/permissions.md +24 -0
  154. package/dist/ui/managed-agents-console/docs/quickstart.md +51 -0
  155. package/dist/ui/managed-agents-console/docs/reference.md +27 -0
  156. package/dist/ui/managed-agents-console/docs/sandbox-reference.md +19 -0
  157. package/dist/ui/managed-agents-console/docs/sandbox-security.md +15 -0
  158. package/dist/ui/managed-agents-console/docs/scheduled-deployments.md +5 -0
  159. package/dist/ui/managed-agents-console/docs/self-hosted-sandboxes.md +15 -0
  160. package/dist/ui/managed-agents-console/docs/session-operations.md +21 -0
  161. package/dist/ui/managed-agents-console/docs/sessions.md +21 -0
  162. package/dist/ui/managed-agents-console/docs/skills.md +15 -0
  163. package/dist/ui/managed-agents-console/docs/tools.md +23 -0
  164. package/dist/ui/managed-agents-console/docs/vaults.md +19 -0
  165. package/dist/ui/managed-agents-console/docs/webhooks.md +11 -0
  166. package/dist/ui/managed-agents-console/index.html +47 -0
  167. package/dist/ui/managed-agents-console/serve.mjs +116 -0
  168. package/dist/ui/managed-agents-console/src/agents-files.jsx +273 -0
  169. package/dist/ui/managed-agents-console/src/api.js +1043 -0
  170. package/dist/ui/managed-agents-console/src/app.jsx +633 -0
  171. package/dist/ui/managed-agents-console/src/auth.jsx +215 -0
  172. package/dist/ui/managed-agents-console/src/console.css +733 -0
  173. package/dist/ui/managed-agents-console/src/data.js +188 -0
  174. package/dist/ui/managed-agents-console/src/detail.jsx +719 -0
  175. package/dist/ui/managed-agents-console/src/docs.jsx +200 -0
  176. package/dist/ui/managed-agents-console/src/environments.jsx +365 -0
  177. package/dist/ui/managed-agents-console/src/forms.jsx +494 -0
  178. package/dist/ui/managed-agents-console/src/icons.jsx +48 -0
  179. package/dist/ui/managed-agents-console/src/skills.jsx +21 -0
  180. package/dist/ui/managed-agents-console/src/sse.js +185 -0
  181. package/dist/ui/managed-agents-console/src/states.jsx +90 -0
  182. package/dist/ui/managed-agents-console/src/tweaks-panel.jsx +541 -0
  183. package/dist/ui/managed-agents-console/src/ui.jsx +190 -0
  184. package/dist/ui/managed-agents-console/src/vaults-data.js +89 -0
  185. package/dist/ui/managed-agents-console/src/vaults.jsx +171 -0
  186. package/dist/ui/managed-agents-console/vendor/babel.min.js +4 -0
  187. package/dist/ui/managed-agents-console/vendor/react-dom.production.min.js +267 -0
  188. package/dist/ui/managed-agents-console/vendor/react.production.min.js +31 -0
  189. package/dist/ui/openapi-docs/VENDOR.md +27 -0
  190. package/dist/ui/openapi-docs/index.html +16 -0
  191. package/dist/ui/openapi-docs/swagger-initializer.js +14 -0
  192. package/dist/ui/openapi-docs/vendor/LICENSE +202 -0
  193. package/dist/ui/openapi-docs/vendor/NOTICE +2 -0
  194. package/dist/ui/openapi-docs/vendor/swagger-ui-bundle.js +2 -0
  195. package/dist/ui/openapi-docs/vendor/swagger-ui-standalone-preset.js +2 -0
  196. package/dist/ui/openapi-docs/vendor/swagger-ui.css +3 -0
  197. package/package.json +53 -0
@@ -0,0 +1,354 @@
1
+ import { chmodSync, closeSync, existsSync, mkdirSync, openSync, readFileSync, realpathSync, unlinkSync, writeFileSync, } from "node:fs";
2
+ import { dirname, resolve } from "node:path";
3
+ import { DatabaseSync } from "node:sqlite";
4
+ import { SqliteAgentStore } from "./agents/store.js";
5
+ import { createBestEffortRuntimeEventCoordinator, createSingleDatabaseRuntimeEventCoordinator, } from "./deployment-runtime-event-coordinator.js";
6
+ import { createInMemorySessionCoordinator, createSingleDatabaseSessionCoordinator, } from "./deployment-session-coordinator.js";
7
+ import { createBestEffortSessionOutputCoordinator, createSingleDatabaseSessionOutputCoordinator, } from "./deployment-session-output-coordinator.js";
8
+ import { SqliteEnvironmentStore } from "./environments/store.js";
9
+ import { EventStore } from "./events/store.js";
10
+ import { InMemoryFileStorage, LocalObjectFileStorage } from "./files/store.js";
11
+ import { loadMasterKey, MASTER_KEY_ENV, MASTER_KEY_FILE_ENV, } from "./secrets/master-key.js";
12
+ import { SqliteSecretsStore } from "./secrets/store.js";
13
+ import { SqliteSessionStore } from "./sessions/store.js";
14
+ import { SqliteVaultStore } from "./vaults/store.js";
15
+ import { InMemorySkillsStore, SqliteSkillsStore } from "./skills/store.js";
16
+ import { SqliteWorkspaceStore } from "./workspaces/store.js";
17
+ // Load the secrets master key only when the operator configured one. Returns
18
+ // undefined when NEITHER env var is set (no-secrets deployment — the store
19
+ // stays absent and the secrets API 4xxs). A malformed/ambiguous key still
20
+ // THROWS (fail-fast): a bad key must fail startup, not silently disable
21
+ // secrets. `loadMasterKey` throws its own "neither set" error, so we guard
22
+ // that one case before delegating.
23
+ function tryLoadMasterKey(env) {
24
+ if (env[MASTER_KEY_ENV] === undefined && env[MASTER_KEY_FILE_ENV] === undefined) {
25
+ return undefined;
26
+ }
27
+ return loadMasterKey({
28
+ [MASTER_KEY_ENV]: env[MASTER_KEY_ENV],
29
+ [MASTER_KEY_FILE_ENV]: env[MASTER_KEY_FILE_ENV],
30
+ });
31
+ }
32
+ function skillLimits(env) {
33
+ return {
34
+ ...positiveEnv("OMA_SKILLS_WORKSPACE_MAX_BYTES", env.OMA_SKILLS_WORKSPACE_MAX_BYTES, "maxWorkspaceBytes"),
35
+ ...positiveEnv("OMA_SKILLS_MAX_VERSIONS", env.OMA_SKILLS_MAX_VERSIONS, "maxVersions"),
36
+ };
37
+ }
38
+ function positiveEnv(name, raw, key) {
39
+ if (raw === undefined)
40
+ return {};
41
+ const value = Number(raw);
42
+ if (!Number.isSafeInteger(value) || value <= 0)
43
+ throw new Error(`${name} must be a positive integer`);
44
+ return { [key]: value };
45
+ }
46
+ export function createDeploymentStoresFromEnv(env) {
47
+ const sqlitePath = normalizeEnvPath(env.OMA_SQLITE_PATH);
48
+ const objectRoot = normalizeEnvPath(env.OMA_FILE_STORAGE_ROOT);
49
+ if ((sqlitePath === undefined) !== (objectRoot === undefined)) {
50
+ throw new Error("OMA_SQLITE_PATH and OMA_FILE_STORAGE_ROOT must be set together for durable deployment storage");
51
+ }
52
+ // Fail-fast on a malformed key regardless of storage mode, before opening
53
+ // any database.
54
+ const masterKey = tryLoadMasterKey(env);
55
+ if (sqlitePath === undefined || objectRoot === undefined) {
56
+ return createInMemoryDeploymentStores(masterKey, env);
57
+ }
58
+ if (sqlitePath === ":memory:") {
59
+ throw new Error("OMA_SQLITE_PATH must be a file path, not :memory:");
60
+ }
61
+ return createDurableDeploymentStores(sqlitePath, objectRoot, masterKey, env);
62
+ }
63
+ function createInMemoryDeploymentStores(masterKey, env) {
64
+ const agents = SqliteAgentStore.open(":memory:");
65
+ const environments = SqliteEnvironmentStore.open(":memory:");
66
+ const sessions = SqliteSessionStore.open(":memory:");
67
+ const events = EventStore.open(":memory:");
68
+ const workspaces = SqliteWorkspaceStore.open(":memory:");
69
+ // Honor the key even in memory so tests can exercise the secrets path. When
70
+ // secrets exist, vault metadata and secrets intentionally share one handle:
71
+ // M2 credential writes rely on one outer transaction spanning both tables.
72
+ const vaultDb = new DatabaseSync(":memory:");
73
+ const secrets = masterKey === undefined ? undefined : new SqliteSecretsStore(vaultDb, masterKey);
74
+ const vaults = new SqliteVaultStore(vaultDb, secrets);
75
+ const files = new InMemoryFileStorage();
76
+ const skills = new InMemorySkillsStore(skillLimits(env));
77
+ const sessionCoordinator = createInMemorySessionCoordinator({
78
+ sessions,
79
+ events,
80
+ });
81
+ const sessionOutputCoordinator = createBestEffortSessionOutputCoordinator({
82
+ sessions,
83
+ events,
84
+ files,
85
+ });
86
+ const runtimeEventCoordinator = createBestEffortRuntimeEventCoordinator({
87
+ sessions,
88
+ events,
89
+ });
90
+ return {
91
+ agents,
92
+ workspaces,
93
+ environments,
94
+ sessions,
95
+ events,
96
+ files,
97
+ skills,
98
+ secrets,
99
+ vaults,
100
+ mode: "memory",
101
+ sessionCoordinator,
102
+ sessionOutputCoordinator,
103
+ runtimeEventCoordinator,
104
+ close: () => {
105
+ agents.close();
106
+ environments.close();
107
+ sessions.close();
108
+ events.close();
109
+ workspaces.close();
110
+ secrets?.scrubMasterKey();
111
+ vaults.close();
112
+ skills.close?.();
113
+ },
114
+ };
115
+ }
116
+ function createDurableDeploymentStores(sqlitePath, objectRoot, masterKey, env) {
117
+ const resolvedSqlitePath = resolve(sqlitePath);
118
+ const requestedObjectRoot = resolve(objectRoot);
119
+ mkdirSync(dirname(resolvedSqlitePath), { recursive: true, mode: 0o700 });
120
+ mkdirSync(requestedObjectRoot, { recursive: true, mode: 0o700 });
121
+ const resolvedObjectRoot = realpathSync(requestedObjectRoot);
122
+ chmodSync(resolvedObjectRoot, 0o700);
123
+ const releaseLock = acquireStorageLock(resolvedSqlitePath, resolvedObjectRoot);
124
+ try {
125
+ const db = new DatabaseSync(resolvedSqlitePath);
126
+ chmodSync(resolvedSqlitePath, 0o600);
127
+ applyDurablePragmas(db);
128
+ ensureStorageBinding(db, resolvedObjectRoot);
129
+ const agents = new SqliteAgentStore(db);
130
+ const environments = new SqliteEnvironmentStore(db);
131
+ const sessions = new SqliteSessionStore(db);
132
+ const events = new EventStore(db);
133
+ const workspaces = new SqliteWorkspaceStore(db);
134
+ // Shares the single durable connection with every other store; its own
135
+ // close() is intentionally NOT wired into close() below, which closes the
136
+ // shared db exactly once.
137
+ const secrets = masterKey === undefined ? undefined : new SqliteSecretsStore(db, masterKey);
138
+ const vaults = new SqliteVaultStore(db, secrets);
139
+ const files = new LocalObjectFileStorage(db, resolvedObjectRoot);
140
+ const skills = new SqliteSkillsStore(db, resolvedObjectRoot, skillLimits(env));
141
+ const sessionCoordinator = createSingleDatabaseSessionCoordinator({
142
+ sessions,
143
+ events,
144
+ files,
145
+ });
146
+ const sessionOutputCoordinator = createSingleDatabaseSessionOutputCoordinator({
147
+ sessions,
148
+ events,
149
+ files,
150
+ });
151
+ const runtimeEventCoordinator = createSingleDatabaseRuntimeEventCoordinator({
152
+ sessions,
153
+ events,
154
+ });
155
+ return {
156
+ agents,
157
+ workspaces,
158
+ environments,
159
+ sessions,
160
+ events,
161
+ files,
162
+ skills,
163
+ secrets,
164
+ vaults,
165
+ mode: "durable",
166
+ sessionCoordinator,
167
+ sessionOutputCoordinator,
168
+ runtimeEventCoordinator,
169
+ sqlitePragmas: () => readSqlitePragmas(db),
170
+ close: () => {
171
+ try {
172
+ secrets?.scrubMasterKey(); // shares db (closed below); scrub the key
173
+ db.close();
174
+ }
175
+ finally {
176
+ releaseLock();
177
+ }
178
+ },
179
+ };
180
+ }
181
+ catch (error) {
182
+ releaseLock();
183
+ throw error;
184
+ }
185
+ }
186
+ // 0113 D8: the provisioning entry point for a live server's database. Opens a
187
+ // second connection with the same durability pragmas — busy_timeout is
188
+ // per-connection, so without it a concurrent server write makes key
189
+ // mint/revoke fail instantly with SQLITE_BUSY. Deliberately does NOT take the
190
+ // .oma.lock: that lock guards single-server ownership (runtime recovery, turn
191
+ // coordination), not table writes; WAL is built for this short-lived writer.
192
+ export function openWorkspaceStoreForProvisioning(sqlitePath) {
193
+ const resolvedSqlitePath = resolve(sqlitePath);
194
+ if (!existsSync(resolvedSqlitePath)) {
195
+ throw new Error(`Provisioning requires an existing OMA database, none found at ${resolvedSqlitePath}. ` +
196
+ "Start the server with OMA_SQLITE_PATH once (or check the path) before provisioning keys.");
197
+ }
198
+ const db = new DatabaseSync(resolvedSqlitePath);
199
+ try {
200
+ // busy_timeout is connection-local and touches nothing on disk; set it
201
+ // before the identity check so the check itself survives contention.
202
+ db.exec("PRAGMA busy_timeout = 5000");
203
+ // Refuse to touch a file the durable server never initialized: a typo'd
204
+ // path to some other SQLite database would otherwise gain OMA auth
205
+ // tables (and even a persistent WAL header flip from the pragmas below),
206
+ // and the minted key would never reach the real server.
207
+ const marker = db
208
+ .prepare(`SELECT name FROM sqlite_master
209
+ WHERE type = 'table' AND name = 'deployment_storage_config'`)
210
+ .get();
211
+ if (marker === undefined) {
212
+ throw new Error(`${resolvedSqlitePath} is not an initialized OMA durable database ` +
213
+ "(missing deployment_storage_config). Start the server once with " +
214
+ "OMA_SQLITE_PATH and OMA_FILE_STORAGE_ROOT before provisioning keys.");
215
+ }
216
+ applyDurablePragmas(db);
217
+ return new SqliteWorkspaceStore(db);
218
+ }
219
+ catch (error) {
220
+ db.close();
221
+ throw error;
222
+ }
223
+ }
224
+ export function applyDurablePragmas(db) {
225
+ db.exec(`
226
+ PRAGMA journal_mode = WAL;
227
+ PRAGMA busy_timeout = 5000;
228
+ PRAGMA foreign_keys = ON;
229
+ PRAGMA synchronous = NORMAL;
230
+ `);
231
+ }
232
+ function readSqlitePragmas(db) {
233
+ return {
234
+ journalMode: db.prepare("PRAGMA journal_mode").get().journal_mode,
235
+ busyTimeout: db.prepare("PRAGMA busy_timeout").get().timeout,
236
+ foreignKeys: db.prepare("PRAGMA foreign_keys").get().foreign_keys,
237
+ synchronous: db.prepare("PRAGMA synchronous").get().synchronous,
238
+ };
239
+ }
240
+ function ensureStorageBinding(db, objectRoot) {
241
+ db.exec(`
242
+ CREATE TABLE IF NOT EXISTS deployment_storage_config (
243
+ key TEXT PRIMARY KEY,
244
+ value TEXT NOT NULL
245
+ )
246
+ `);
247
+ db.prepare(`INSERT OR IGNORE INTO deployment_storage_config (key, value)
248
+ VALUES ('object_root', ?)`).run(objectRoot);
249
+ const row = db.prepare(`SELECT value FROM deployment_storage_config WHERE key = 'object_root'`).get();
250
+ if (row?.value !== objectRoot) {
251
+ throw new Error(`OMA_SQLITE_PATH is already bound to object storage root ${row?.value ?? "<missing>"}. ` +
252
+ `Refusing to open it with ${objectRoot}.`);
253
+ }
254
+ }
255
+ function normalizeEnvPath(value) {
256
+ if (value === undefined)
257
+ return undefined;
258
+ const trimmed = value.trim();
259
+ if (trimmed.length === 0) {
260
+ throw new Error("Durable storage env vars must not be empty");
261
+ }
262
+ return trimmed;
263
+ }
264
+ function acquireStorageLock(sqlitePath, objectRoot) {
265
+ const lockPath = `${sqlitePath}.oma.lock`;
266
+ let fd = openLockFile(lockPath, objectRoot);
267
+ let released = false;
268
+ return () => {
269
+ if (released)
270
+ return;
271
+ released = true;
272
+ closeSync(fd);
273
+ unlinkIfExistsSync(lockPath);
274
+ };
275
+ }
276
+ function openLockFile(lockPath, objectRoot) {
277
+ try {
278
+ return createLockFile(lockPath, objectRoot);
279
+ }
280
+ catch (error) {
281
+ if (error.code === "EEXIST") {
282
+ if (removeStaleLock(lockPath)) {
283
+ return createLockFile(lockPath, objectRoot);
284
+ }
285
+ throw lockedStorageError(lockPath);
286
+ }
287
+ throw error;
288
+ }
289
+ }
290
+ function createLockFile(lockPath, objectRoot) {
291
+ const fd = openSync(lockPath, "wx", 0o600);
292
+ try {
293
+ writeFileSync(fd, JSON.stringify({
294
+ pid: process.pid,
295
+ objectRoot,
296
+ createdAt: new Date().toISOString(),
297
+ }));
298
+ return fd;
299
+ }
300
+ catch (error) {
301
+ closeSync(fd);
302
+ unlinkIfExistsSync(lockPath);
303
+ throw error;
304
+ }
305
+ }
306
+ function removeStaleLock(lockPath) {
307
+ const pid = readLockPid(lockPath);
308
+ if (pid === undefined || isProcessRunning(pid))
309
+ return false;
310
+ try {
311
+ unlinkSync(lockPath);
312
+ return true;
313
+ }
314
+ catch (error) {
315
+ if (error.code === "ENOENT")
316
+ return true;
317
+ throw error;
318
+ }
319
+ }
320
+ function readLockPid(lockPath) {
321
+ if (!existsSync(lockPath))
322
+ return undefined;
323
+ try {
324
+ const parsed = JSON.parse(readFileSync(lockPath, "utf8"));
325
+ return typeof parsed.pid === "number" && Number.isInteger(parsed.pid)
326
+ ? parsed.pid
327
+ : undefined;
328
+ }
329
+ catch {
330
+ return undefined;
331
+ }
332
+ }
333
+ function isProcessRunning(pid) {
334
+ try {
335
+ process.kill(pid, 0);
336
+ return true;
337
+ }
338
+ catch (error) {
339
+ return error.code === "EPERM";
340
+ }
341
+ }
342
+ function lockedStorageError(lockPath) {
343
+ return new Error(`Durable storage database is already locked: ${lockPath}. ` +
344
+ "OMA durable mode requires exclusive process access; stop the other process before reusing this SQLite path.");
345
+ }
346
+ function unlinkIfExistsSync(path) {
347
+ try {
348
+ unlinkSync(path);
349
+ }
350
+ catch (error) {
351
+ if (error.code !== "ENOENT")
352
+ throw error;
353
+ }
354
+ }
@@ -0,0 +1,48 @@
1
+ /** Dedicated, public, multi-platform OMA egress sidecar (plan 0141). */
2
+ export const DEFAULT_OMA_EGRESS_SIDECAR_IMAGE = "ghcr.io/oneryalcin/open-managed-agents-sandbox@sha256:2b2a385584fc4f428d541edb930b93ecba5f029576db737fea605fb159994974";
3
+ const KEYS = [
4
+ "OMA_ENABLE_EGRESS",
5
+ "OMA_EGRESS_SIDECAR_IMAGE",
6
+ "OMA_EGRESS_SIDECAR_REPO_MOUNT",
7
+ ];
8
+ /**
9
+ * Resolve the `oma up` egress environment without silently completing a
10
+ * partial operator override from OMA defaults.
11
+ */
12
+ export function resolveOmaUpEgressEnvironment(input, sandbox) {
13
+ const output = { ...input };
14
+ for (const key of KEYS)
15
+ delete output[key];
16
+ const enabled = optional(input.OMA_ENABLE_EGRESS);
17
+ const image = optional(input.OMA_EGRESS_SIDECAR_IMAGE);
18
+ const repoMount = optional(input.OMA_EGRESS_SIDECAR_REPO_MOUNT);
19
+ const hasAnyOverride = enabled !== undefined || image !== undefined || repoMount !== undefined;
20
+ if (sandbox !== "docker-local") {
21
+ if (hasAnyOverride) {
22
+ throw new Error("OMA egress overrides require `oma up --sandbox docker`");
23
+ }
24
+ return output;
25
+ }
26
+ if (!hasAnyOverride) {
27
+ output.OMA_ENABLE_EGRESS = "true";
28
+ output.OMA_EGRESS_SIDECAR_IMAGE = DEFAULT_OMA_EGRESS_SIDECAR_IMAGE;
29
+ return output;
30
+ }
31
+ if (enabled === "false" && image === undefined && repoMount === undefined) {
32
+ output.OMA_ENABLE_EGRESS = "false";
33
+ return output;
34
+ }
35
+ if (enabled !== "true" || image === undefined) {
36
+ throw new Error("Custom egress configuration requires OMA_ENABLE_EGRESS=true and OMA_EGRESS_SIDECAR_IMAGE together; use OMA_ENABLE_EGRESS=false alone to disable it");
37
+ }
38
+ output.OMA_ENABLE_EGRESS = "true";
39
+ output.OMA_EGRESS_SIDECAR_IMAGE = image;
40
+ if (repoMount !== undefined)
41
+ output.OMA_EGRESS_SIDECAR_REPO_MOUNT = repoMount;
42
+ return output;
43
+ }
44
+ function optional(value) {
45
+ if (value === undefined || value.trim() === "")
46
+ return undefined;
47
+ return value.trim();
48
+ }