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
package/bin/oma.mjs ADDED
@@ -0,0 +1,499 @@
1
+ #!/usr/bin/env node
2
+ import { spawn, spawnSync } from "node:child_process";
3
+ import { randomBytes } from "node:crypto";
4
+ import { chmodSync, existsSync, mkdirSync, readFileSync, statSync, writeFileSync } from "node:fs";
5
+ import { homedir } from "node:os";
6
+ import { dirname, join } from "node:path";
7
+ import { fileURLToPath, pathToFileURL } from "node:url";
8
+
9
+ const root = join(dirname(fileURLToPath(import.meta.url)), "..");
10
+ const compiledRoot = join(root, "dist");
11
+ const usesCompiledRuntime = existsSync(join(compiledRoot, "src", "main.js"));
12
+ const runtimeRoot = usesCompiledRuntime ? compiledRoot : root;
13
+ const runtimeExtension = usesCompiledRuntime ? ".js" : ".ts";
14
+ const packageJson = JSON.parse(readFileSync(join(root, "package.json"), "utf8"));
15
+ const args = process.argv.slice(2);
16
+
17
+ const { resolveOmaUpEgressEnvironment } = await import(
18
+ pathToFileURL(runtimeFile("src", "control-plane", "egress", "image")).href,
19
+ );
20
+
21
+ checkNode();
22
+
23
+ const command = args[0];
24
+ if (command === undefined || command === "help" || command === "--help" || command === "-h") {
25
+ if (command === "help" && args.length > 1) {
26
+ printCommandHelp(args.slice(1));
27
+ process.exit(0);
28
+ }
29
+ printHelp();
30
+ process.exit(0);
31
+ }
32
+ if ((command === "version" && args.length === 1) || command === "--version" || command === "-v") {
33
+ console.log(`oma ${packageJson.version}`);
34
+ process.exit(0);
35
+ }
36
+ const commandArgs = args.slice(1);
37
+ const helpIndex = commandArgs.findIndex((arg) => arg === "--help" || arg === "-h");
38
+ if (helpIndex !== -1) {
39
+ if (helpIndex !== commandArgs.length - 1) fail(`Help must be the last argument. Run \`oma help ${[command, ...commandArgs.slice(0, helpIndex)].join(" ")}\`.`);
40
+ printCommandHelp([command, ...commandArgs.slice(0, helpIndex)]);
41
+ process.exit(0);
42
+ }
43
+
44
+ if (command === "up") {
45
+ await runUp(args.slice(1));
46
+ } else if (command === "smoke") {
47
+ await runSmoke(args.slice(1));
48
+ } else if (command === "keys") {
49
+ await runKeys(args.slice(1));
50
+ } else if (command === "workspaces") {
51
+ await runWorkspaces(args.slice(1));
52
+ } else if (command === "admin") {
53
+ runAdmin(args.slice(1));
54
+ } else if (command === "providers") {
55
+ await runProviders(args.slice(1));
56
+ } else if (command === "models") {
57
+ await runModels(args.slice(1));
58
+ } else if (command === "auth") {
59
+ await runAuth(args.slice(1));
60
+ } else if (command === "doctor") {
61
+ await runDoctor(args.slice(1));
62
+ } else if (command === "down" || command === "logs" || command === "status") {
63
+ fail(`${command} is not implemented yet. Run \`oma up\` in the foreground and use Ctrl-C to stop it.`);
64
+ } else {
65
+ fail(`Unknown command: ${command}\n\nRun \`oma --help\` for usage.`);
66
+ }
67
+
68
+ async function runDoctor(commandArgs) {
69
+ await runChild(
70
+ process.execPath,
71
+ [
72
+ ...runtimeNodeFlags(),
73
+ runtimeFile("scripts", "oma-doctor"),
74
+ ...commandArgs,
75
+ ],
76
+ process.env,
77
+ );
78
+ }
79
+
80
+ async function runUp(commandArgs) {
81
+ let sandbox = "docker-local";
82
+ for (let index = 0; index < commandArgs.length; index += 1) {
83
+ const arg = commandArgs[index];
84
+ if (arg === "--sandbox") {
85
+ const value = commandArgs[index + 1];
86
+ if (value === undefined) fail("--sandbox requires docker or microsandbox");
87
+ sandbox = normalizeSandbox(value);
88
+ index += 1;
89
+ continue;
90
+ }
91
+ if (arg === "--detach" || arg === "-d") {
92
+ fail("Detached mode is not implemented yet. Run `oma up` in the foreground for now.");
93
+ }
94
+ fail(`Unknown option for oma up: ${arg}\nRun \`oma help up\` for usage.`);
95
+ }
96
+
97
+ checkSandbox(sandbox);
98
+ let runtimeEnv;
99
+ try {
100
+ runtimeEnv = resolveOmaUpEgressEnvironment(process.env, sandbox);
101
+ } catch (error) {
102
+ fail(error instanceof Error ? error.message : String(error));
103
+ }
104
+ const sandboxEnv = sandbox === "docker-local"
105
+ ? { OMA_SANDBOX_PROVIDER: sandbox, OMA_ALLOW_DOCKER_LOCAL: "true" }
106
+ : { OMA_SANDBOX_PROVIDER: sandbox, OMA_ALLOW_MICROSANDBOX_LOCAL: "true" };
107
+
108
+ const adminEnv = resolveAdminEnvironment();
109
+ console.log(`Starting OMA with ${sandbox}`);
110
+ if (runtimeEnv.OMA_ENABLE_EGRESS === "true") {
111
+ console.log(`Approved HTTPS egress: ${runtimeEnv.OMA_EGRESS_SIDECAR_IMAGE}`);
112
+ } else {
113
+ console.log("Approved HTTPS egress: unavailable (offline environments only)");
114
+ }
115
+ if (adminEnv.OMA_ADMIN_KEY_FILE !== undefined) {
116
+ console.log(`Admin mode: ${adminEnv.OMA_ADMIN_KEY_FILE}`);
117
+ } else if (process.env.OMA_ADMIN_KEY !== undefined) {
118
+ console.log("Admin mode: OMA_ADMIN_KEY environment variable");
119
+ }
120
+ console.log("Press Ctrl-C to stop. Durable data defaults to ~/.oma.\n");
121
+ await runChild(
122
+ process.execPath,
123
+ [
124
+ ...runtimeNodeFlags(),
125
+ runtimeFile("src", "main"),
126
+ ],
127
+ { ...runtimeEnv, ...sandboxEnv, ...adminEnv },
128
+ );
129
+ }
130
+
131
+ function runAdmin(commandArgs) {
132
+ const [action, ...rest] = commandArgs;
133
+ if (action === "init") {
134
+ let path = defaultAdminKeyPath();
135
+ for (let index = 0; index < rest.length; index += 1) {
136
+ const arg = rest[index];
137
+ if (arg === "--file") {
138
+ path = requiredOption(rest, ++index, arg);
139
+ } else {
140
+ fail(`Unknown option for oma admin init: ${arg}\nRun \`oma help admin init\` for usage.`);
141
+ }
142
+ }
143
+ mkdirSync(dirname(path), { recursive: true, mode: 0o700 });
144
+ const key = randomBytes(32).toString("base64");
145
+ try {
146
+ writeFileSync(path, `${key}\n`, { encoding: "utf8", flag: "wx", mode: 0o600 });
147
+ chmodSync(path, 0o600);
148
+ } catch (error) {
149
+ if (error?.code === "EEXIST") {
150
+ fail(`Admin key file already exists at ${path}. Refusing to overwrite it.`);
151
+ }
152
+ throw error;
153
+ }
154
+ console.log(`Admin key initialized at ${path}`);
155
+ console.log(`Admin key: ${key}`);
156
+ console.log("This key grants appliance-wide administration. Store it securely.");
157
+ console.log("Restart `oma up` to enable console admin mode.");
158
+ return;
159
+ }
160
+ if (action === "status") {
161
+ if (rest.length !== 0) fail("Usage: oma admin status");
162
+ if (process.env.OMA_ADMIN_KEY !== undefined && process.env.OMA_ADMIN_KEY !== "") {
163
+ console.log("Admin mode is configured through OMA_ADMIN_KEY.");
164
+ return;
165
+ }
166
+ const path = defaultAdminKeyPath();
167
+ if (!existsSync(path)) {
168
+ console.log(`Admin mode is not configured. Run \`oma admin init\` to create ${path}.`);
169
+ return;
170
+ }
171
+ const mode = statSync(path).mode & 0o777;
172
+ console.log(`Admin key file: ${path}`);
173
+ console.log(`Permissions: ${mode.toString(8).padStart(3, "0")}`);
174
+ console.log("Admin mode will be enabled the next time `oma up` starts.");
175
+ return;
176
+ }
177
+ if (action === "rotate") {
178
+ fail("Admin key rotation is not implemented yet. Preserve the existing key file.");
179
+ }
180
+ fail("Usage: oma admin <init|status>");
181
+ }
182
+
183
+ async function runKeys(commandArgs) {
184
+ const [action, ...rest] = commandArgs;
185
+ if (action !== "mint" && action !== "list") {
186
+ fail("Usage: oma keys <mint|list> [--workspace id] [--label label] [--db path]");
187
+ }
188
+ let workspace = "wrk_default";
189
+ let label = "oma-cli";
190
+ let db = defaultDatabasePath();
191
+ for (let index = 0; index < rest.length; index += 1) {
192
+ const arg = rest[index];
193
+ if (arg === "--workspace") {
194
+ workspace = requiredOption(rest, ++index, arg);
195
+ } else if (arg === "--label" && action === "mint") {
196
+ label = requiredOption(rest, ++index, arg);
197
+ } else if (arg === "--db") {
198
+ db = requiredOption(rest, ++index, arg);
199
+ } else {
200
+ fail(`Unknown option for oma keys ${action}: ${arg}\nRun \`oma help keys ${action}\` for usage.`);
201
+ }
202
+ }
203
+ const provisioningArgs = action === "mint"
204
+ ? ["mint-key", workspace, label, "--db", db]
205
+ : ["list-keys", workspace, "--db", db];
206
+ await runProvisioning(provisioningArgs);
207
+ }
208
+
209
+ async function runWorkspaces(commandArgs) {
210
+ const [action, ...rest] = commandArgs;
211
+ if (action !== "list") fail("Usage: oma workspaces list [--db path]");
212
+ let db = defaultDatabasePath();
213
+ for (let index = 0; index < rest.length; index += 1) {
214
+ const arg = rest[index];
215
+ if (arg === "--db") {
216
+ db = requiredOption(rest, ++index, arg);
217
+ } else {
218
+ fail(`Unknown option for oma workspaces list: ${arg}\nRun \`oma help workspaces list\` for usage.`);
219
+ }
220
+ }
221
+ await runProvisioning(["list-workspaces", "--db", db]);
222
+ }
223
+
224
+ async function runProviders(commandArgs) {
225
+ const [action, ...rest] = commandArgs;
226
+ if (action !== "status") fail("Usage: oma providers status");
227
+ if (rest.length !== 0) fail("Usage: oma providers status");
228
+ await runModelProvisioning(["providers-status"]);
229
+ }
230
+
231
+ async function runModels(commandArgs) {
232
+ const [action, ...rest] = commandArgs;
233
+ if (action === "list") {
234
+ await runModelProvisioning(["models-list", ...rest]);
235
+ return;
236
+ }
237
+ if (action === "validate") {
238
+ await runModelProvisioning(["models-validate", ...rest]);
239
+ return;
240
+ }
241
+ fail("Usage: oma models <list|validate>");
242
+ }
243
+
244
+ async function runAuth(commandArgs) {
245
+ const [action, ...rest] = commandArgs;
246
+ if (action === "set") {
247
+ await runModelProvisioning(["auth-set", ...rest]);
248
+ return;
249
+ }
250
+ if (action === "remove") {
251
+ await runModelProvisioning(["auth-remove", ...rest]);
252
+ return;
253
+ }
254
+ if (action === "status") {
255
+ await runModelProvisioning(["auth-status", ...rest]);
256
+ return;
257
+ }
258
+ fail("Usage: oma auth <set|remove|status>");
259
+ }
260
+
261
+ async function runProvisioning(provisioningArgs) {
262
+ await runChild(
263
+ process.execPath,
264
+ [
265
+ ...runtimeNodeFlags(),
266
+ runtimeFile("scripts", "oma-workspaces"),
267
+ ...provisioningArgs,
268
+ ],
269
+ process.env,
270
+ );
271
+ }
272
+
273
+ async function runModelProvisioning(provisioningArgs) {
274
+ await runChild(
275
+ process.execPath,
276
+ [
277
+ ...runtimeNodeFlags(),
278
+ runtimeFile("scripts", "oma-models"),
279
+ ...provisioningArgs,
280
+ ],
281
+ process.env,
282
+ );
283
+ }
284
+
285
+ async function runSmoke(commandArgs) {
286
+ let sandbox;
287
+ let localCompatible = false;
288
+ let egress = false;
289
+ for (let index = 0; index < commandArgs.length; index += 1) {
290
+ const arg = commandArgs[index];
291
+ if (arg === "--sandbox") {
292
+ const value = commandArgs[index + 1];
293
+ if (value === undefined) fail("--sandbox requires docker or microsandbox");
294
+ sandbox = normalizeSandbox(value);
295
+ index += 1;
296
+ continue;
297
+ }
298
+ if (arg === "--local-compatible") {
299
+ localCompatible = true;
300
+ continue;
301
+ }
302
+ if (arg === "--egress") {
303
+ egress = true;
304
+ localCompatible = true;
305
+ continue;
306
+ }
307
+ fail(`Unknown option for oma smoke: ${arg}\nRun \`oma help smoke\` for usage.`);
308
+ }
309
+ await runChild(
310
+ process.execPath,
311
+ [join(runtimeRoot, "scripts", "alpha-smoke.mjs")],
312
+ {
313
+ ...process.env,
314
+ ...(sandbox === undefined ? {} : { OMA_ALPHA_SANDBOX_PROVIDER: sandbox }),
315
+ ...(localCompatible ? { OMA_ALPHA_LOCAL_COMPATIBLE: "1" } : {}),
316
+ ...(egress ? { OMA_ALPHA_EGRESS_SMOKE: "1" } : {}),
317
+ },
318
+ );
319
+ }
320
+
321
+ function runtimeFile(...segments) {
322
+ const stem = join(runtimeRoot, ...segments);
323
+ return `${stem}${runtimeExtension}`;
324
+ }
325
+
326
+ function runtimeNodeFlags() {
327
+ return usesCompiledRuntime ? [] : ["--experimental-transform-types", "--disable-warning=ExperimentalWarning"];
328
+ }
329
+
330
+ function requiredOption(args, index, option) {
331
+ const value = args[index];
332
+ if (value === undefined || value.startsWith("--")) fail(`${option} requires a value`);
333
+ return value;
334
+ }
335
+
336
+ function defaultDatabasePath() {
337
+ return process.env.OMA_SQLITE_PATH ?? join(defaultOmaHome(), "oma.sqlite");
338
+ }
339
+
340
+ function defaultOmaHome() {
341
+ return process.env.OMA_HOME ?? join(homedir(), ".oma");
342
+ }
343
+
344
+ function defaultAdminKeyPath() {
345
+ return process.env.OMA_ADMIN_KEY_FILE ?? join(defaultOmaHome(), "admin.key");
346
+ }
347
+
348
+ function resolveAdminEnvironment() {
349
+ if (process.env.OMA_ADMIN_KEY !== undefined || process.env.OMA_ADMIN_KEY_FILE !== undefined) return {};
350
+ const path = defaultAdminKeyPath();
351
+ return existsSync(path) ? { OMA_ADMIN_KEY_FILE: path } : {};
352
+ }
353
+
354
+ function normalizeSandbox(value) {
355
+ if (value === "docker" || value === "docker-local") return "docker-local";
356
+ if (value === "microsandbox" || value === "microsandbox-local") return "microsandbox-local";
357
+ fail(`Unsupported sandbox ${JSON.stringify(value)}. Use docker or microsandbox.`);
358
+ }
359
+
360
+ function checkSandbox(sandbox) {
361
+ if (sandbox === "docker-local") {
362
+ const result = spawnSync("docker", ["info"], { encoding: "utf8" });
363
+ if (result.status !== 0) {
364
+ const detail = (result.stderr || result.stdout || "docker info failed").trim();
365
+ fail(`Docker is required for \`oma up\`. Start Docker and retry.\n${detail}`);
366
+ }
367
+ return;
368
+ }
369
+ const command = process.env.OMA_MICROSANDBOX_COMMAND ?? "msb";
370
+ const result = spawnSync(command, ["--version"], { encoding: "utf8" });
371
+ if (result.status !== 0) {
372
+ const detail = (result.stderr || result.stdout || `${command} --version failed`).trim();
373
+ fail(`Microsandbox is required for \`oma up --sandbox microsandbox\`.\n${detail}`);
374
+ }
375
+ }
376
+
377
+ function runChild(command, childArgs, env) {
378
+ return new Promise((resolve) => {
379
+ const child = spawn(command, childArgs, { cwd: root, env, stdio: "inherit" });
380
+ const onSigint = () => child.exitCode === null && child.kill("SIGINT");
381
+ const onSigterm = () => child.exitCode === null && child.kill("SIGTERM");
382
+ process.on("SIGINT", onSigint);
383
+ process.on("SIGTERM", onSigterm);
384
+ child.on("exit", (code, signal) => {
385
+ process.off("SIGINT", onSigint);
386
+ process.off("SIGTERM", onSigterm);
387
+ if (signal !== null) {
388
+ process.exit(signal === "SIGINT" ? 130 : 143);
389
+ }
390
+ if ((code ?? 0) !== 0) process.exit(code ?? 1);
391
+ resolve();
392
+ });
393
+ });
394
+ }
395
+
396
+ function checkNode() {
397
+ const [major = 0, minor = 0] = process.versions.node.split(".").map((part) => Number.parseInt(part, 10));
398
+ if (major < 22 || (major === 22 && minor < 19)) {
399
+ fail(`oma requires Node >= 22.19.0 (found ${process.versions.node})`);
400
+ }
401
+ }
402
+
403
+ function printHelp() {
404
+ console.log(`Open Managed Agents CLI
405
+
406
+ Usage:
407
+ oma up [--sandbox docker|microsandbox]
408
+ oma smoke [--sandbox docker|microsandbox] [--local-compatible] [--egress]
409
+ oma keys mint [--workspace id] [--label label]
410
+ oma keys list [--workspace id]
411
+ oma workspaces list
412
+ oma providers status
413
+ oma models list [--provider name] [--available]
414
+ oma models validate [--file path]
415
+ oma auth set <provider> [--stdin]
416
+ oma auth status [provider]
417
+ oma auth remove <provider>
418
+ oma doctor [--sandbox docker|microsandbox] [--json]
419
+ oma admin init
420
+ oma admin status
421
+ oma version
422
+ oma help
423
+
424
+ Commands:
425
+ up Start the durable local appliance in the foreground. Docker is the default.
426
+ smoke Run the disposable end-to-end alpha smoke test.
427
+ keys Mint or list workspace API keys in the local appliance database.
428
+ workspaces List local appliance workspaces.
429
+ providers Inspect enabled Pi model providers and credential readiness.
430
+ models List or validate enabled Pi models.
431
+ auth Store, remove, or inspect model provider API-key credentials.
432
+ doctor Run secret-safe, read-only local readiness diagnostics.
433
+ admin Initialize or inspect local appliance admin mode.
434
+ version Print the installed OMA version.
435
+
436
+ Environment:
437
+ ANTHROPIC_API_KEY Anthropic model credential used by the control plane.
438
+ OMA_MODEL_PROVIDERS Enabled provider allowlist (default: anthropic,openai,openrouter).
439
+ OMA_DEFAULT_MODEL_PROVIDER / OMA_DEFAULT_MODEL
440
+ Default exact model pair.
441
+ OMA_ALPHA_MODEL_PROVIDER / OMA_ALPHA_MODEL
442
+ Exact provider/model used by oma smoke.
443
+ OMA_PI_AUTH_FILE Pi auth storage path (default: $OMA_HOME/pi/auth.json).
444
+ OMA_PI_MODELS_FILE Pi models.json path (default: $OMA_HOME/pi/models.json).
445
+ OMA_HOME Durable data directory (default: ~/.oma).
446
+ OMA_HOST Bind address (default: 127.0.0.1).
447
+ OMA_PORT Listen port (default: 4180).
448
+
449
+ Exit status:
450
+ 0 Command or help completed successfully.
451
+ 1 A readiness or runtime verification failed.
452
+ 2 The invocation or local configuration was invalid.
453
+ 130/143 Interrupted by SIGINT/SIGTERM.
454
+
455
+ Planned, not implemented yet:
456
+ oma up --detach
457
+ oma status
458
+ oma logs
459
+ oma down
460
+ oma admin rotate`);
461
+ }
462
+
463
+ function commandHelpFor(key) {
464
+ return ({
465
+ up: ["Usage: oma up [--sandbox docker|microsandbox]", "Start OMA in the foreground. Docker is the default and includes the pinned HTTPS egress sidecar capability; environments remain default-deny.", "Examples: oma up; OMA_ENABLE_EGRESS=false oma up; oma up --sandbox microsandbox"],
466
+ smoke: ["Usage: oma smoke [--sandbox docker|microsandbox] [--local-compatible] [--egress]", "Run the disposable end-to-end alpha proof. --egress uses Docker plus the deterministic local-compatible model fixture to verify npm, PyPI/uv, GitHub, denial, and cleanup.", "Examples: oma smoke --local-compatible; oma smoke --egress"],
467
+ keys: ["Usage: oma keys <mint|list> [options]", "Manage workspace API keys in the local SQLite database.", "Examples: oma keys mint --workspace wrk_default; oma keys list --workspace wrk_default"],
468
+ "keys mint": ["Usage: oma keys mint [--workspace id] [--label label] [--db path]", "Mint a workspace key and print its plaintext once.", "Example: oma keys mint --workspace wrk_default --label console"],
469
+ "keys list": ["Usage: oma keys list [--workspace id] [--db path]", "List workspace key metadata without plaintext secrets.", "Example: oma keys list --workspace wrk_default"],
470
+ workspaces: ["Usage: oma workspaces list [--db path]", "List local workspaces.", "Example: oma workspaces list"],
471
+ "workspaces list": ["Usage: oma workspaces list [--db path]", "List local workspaces.", "Example: oma workspaces list --db ~/.oma/oma.sqlite"],
472
+ providers: ["Usage: oma providers status", "Inspect enabled Pi providers and credential readiness.", "Example: oma providers status"],
473
+ "providers status": ["Usage: oma providers status", "Inspect enabled Pi providers and credential readiness.", "Example: oma providers status"],
474
+ models: ["Usage: oma models <list|validate>", "Discover enabled models or validate models.json.", "Examples: oma models list --available; oma models validate"],
475
+ "models list": ["Usage: oma models list [--provider name] [--available]", "List enabled Pi models without exposing credentials.", "Example: oma models list --provider openai --available"],
476
+ "models validate": ["Usage: oma models validate [--file path]", "Validate deployment policy and Pi models.json without calling a provider.", "Example: oma models validate --file ~/.oma/pi/models.json"],
477
+ auth: ["Usage: oma auth <set|status|remove>", "Manage provider API-key credentials.", "Examples: printf '%s\\n' \"$OPENAI_API_KEY\" | oma auth set openai --stdin; oma auth status"],
478
+ "auth set": ["Usage: oma auth set <provider> [--stdin]", "Store a provider key without accepting it as a command-line argument.", "Example: printf '%s\\n' \"$OPENAI_API_KEY\" | oma auth set openai --stdin"],
479
+ "auth status": ["Usage: oma auth status [provider]", "Show credential readiness without secret values.", "Example: oma auth status openai"],
480
+ "auth remove": ["Usage: oma auth remove <provider>", "Remove a stored provider credential idempotently.", "Example: oma auth remove openai"],
481
+ admin: ["Usage: oma admin <init|status>", "Initialize or inspect appliance-wide admin mode.", "Examples: oma admin init; oma admin status"],
482
+ "admin init": ["Usage: oma admin init [--file path]", "Create a new owner-only admin key file; existing files are never overwritten.", "Example: oma admin init"],
483
+ "admin status": ["Usage: oma admin status", "Inspect admin configuration without printing the key.", "Example: oma admin status"],
484
+ doctor: ["Usage: oma doctor [--sandbox docker|microsandbox] [--json]", "Run read-only readiness checks. This command creates no OMA files and downloads no images.", "Examples: oma doctor; oma doctor --json", "Exit codes: 0 ready (warnings allowed), 1 readiness failures, 2 invalid invocation or internal diagnostic failure."],
485
+ version: ["Usage: oma version", "Print the installed OMA package version.", "Example: oma version"],
486
+ })[key];
487
+ }
488
+
489
+ function printCommandHelp(parts) {
490
+ const key = parts.join(" ");
491
+ const lines = commandHelpFor(key);
492
+ if (lines === undefined) fail(`Unknown help topic: ${key}\n\nRun \`oma --help\` for available commands.`);
493
+ console.log(`${lines.join("\n\n")}\n\nExit status: 0 success/help, 1 readiness or runtime failure, 2 invalid invocation/configuration, 130/143 interruption.\n\nAll command help exits 0 and performs no storage or network mutation.`);
494
+ }
495
+
496
+ function fail(message) {
497
+ console.error(`oma: ${message}`);
498
+ process.exit(2);
499
+ }
@@ -0,0 +1,134 @@
1
+ import { createServer } from "node:http";
2
+
3
+ const MAX_REQUEST_BYTES = 1024 * 1024;
4
+
5
+ export async function startAlphaOpenAICompatibleFixture(options) {
6
+ const requests = [];
7
+ const server = createServer(async (req, res) => {
8
+ try {
9
+ if (req.method !== "POST" || req.url !== "/v1/chat/completions") {
10
+ sendJson(res, 404, { error: { message: "not found" } });
11
+ return;
12
+ }
13
+ const body = JSON.parse(await readBody(req));
14
+ const authorization = req.headers.authorization;
15
+ requests.push({ body, authorization });
16
+ if (authorization !== `Bearer ${options.apiKey}`) {
17
+ sendJson(res, 401, { error: { message: "fixture rejected authorization" } });
18
+ return;
19
+ }
20
+ if (body.model !== options.modelId || body.stream !== true) {
21
+ sendJson(res, 400, { error: { message: "fixture received unexpected model request" } });
22
+ return;
23
+ }
24
+
25
+ const messages = Array.isArray(body.messages) ? body.messages : [];
26
+ const hasToolResult = messages.some((message) => message?.role === "tool");
27
+ if (!hasToolResult) {
28
+ const bash = Array.isArray(body.tools)
29
+ && body.tools.some((tool) => tool?.type === "function" && tool.function?.name === "bash");
30
+ if (!bash) {
31
+ sendJson(res, 400, { error: { message: "fixture expected the bash tool" } });
32
+ return;
33
+ }
34
+ sendSse(res, [
35
+ chunk(options.modelId, {
36
+ role: "assistant",
37
+ tool_calls: [{
38
+ index: 0,
39
+ id: "call_oma_alpha_smoke",
40
+ type: "function",
41
+ function: {
42
+ name: "bash",
43
+ arguments: JSON.stringify({ command: options.command ?? `printf ${options.token}` }),
44
+ },
45
+ }],
46
+ }),
47
+ chunk(options.modelId, {}, "tool_calls", usage()),
48
+ ]);
49
+ return;
50
+ }
51
+
52
+ if (!JSON.stringify(messages).includes(options.token)) {
53
+ sendJson(res, 400, { error: { message: "fixture did not receive the expected tool result" } });
54
+ return;
55
+ }
56
+ sendSse(res, [
57
+ chunk(options.modelId, { role: "assistant", content: options.token }),
58
+ chunk(options.modelId, {}, "stop", usage()),
59
+ ]);
60
+ } catch (error) {
61
+ sendJson(res, 500, { error: { message: error instanceof Error ? error.message : String(error) } });
62
+ }
63
+ });
64
+
65
+ await new Promise((resolve, reject) => {
66
+ server.once("error", reject);
67
+ server.listen(0, "127.0.0.1", () => {
68
+ server.off("error", reject);
69
+ resolve();
70
+ });
71
+ });
72
+ const address = server.address();
73
+ if (!address || typeof address === "string") throw new Error("Could not resolve fixture address");
74
+
75
+ return {
76
+ baseUrl: `http://127.0.0.1:${address.port}`,
77
+ assertComplete() {
78
+ if (requests.length !== 2) {
79
+ throw new Error(`Compatible-provider fixture expected 2 model requests, observed ${requests.length}`);
80
+ }
81
+ if (!requests[1]?.body?.messages?.some((message) => message?.role === "tool")) {
82
+ throw new Error("Compatible-provider fixture did not observe the sandbox tool result round trip");
83
+ }
84
+ },
85
+ requestSummary() {
86
+ return requests.map(({ body }) => ({ model: body.model, stream: body.stream }));
87
+ },
88
+ async close() {
89
+ await new Promise((resolve) => server.close(resolve));
90
+ },
91
+ };
92
+ }
93
+
94
+ function chunk(model, delta, finishReason = null, chunkUsage) {
95
+ return {
96
+ id: "chatcmpl-oma-alpha",
97
+ object: "chat.completion.chunk",
98
+ created: 1,
99
+ model,
100
+ choices: [{ index: 0, delta, finish_reason: finishReason }],
101
+ ...(chunkUsage === undefined ? {} : { usage: chunkUsage }),
102
+ };
103
+ }
104
+
105
+ function usage() {
106
+ return { prompt_tokens: 10, completion_tokens: 3, total_tokens: 13 };
107
+ }
108
+
109
+ function sendSse(res, chunks) {
110
+ res.writeHead(200, {
111
+ "content-type": "text/event-stream; charset=utf-8",
112
+ "cache-control": "no-cache",
113
+ connection: "keep-alive",
114
+ });
115
+ for (const value of chunks) res.write(`data: ${JSON.stringify(value)}\n\n`);
116
+ res.end("data: [DONE]\n\n");
117
+ }
118
+
119
+ function sendJson(res, status, body) {
120
+ if (res.headersSent) return;
121
+ res.writeHead(status, { "content-type": "application/json" });
122
+ res.end(JSON.stringify(body));
123
+ }
124
+
125
+ async function readBody(req) {
126
+ const chunks = [];
127
+ let bytes = 0;
128
+ for await (const chunk of req) {
129
+ bytes += chunk.length;
130
+ if (bytes > MAX_REQUEST_BYTES) throw new Error("fixture request exceeded 1 MiB");
131
+ chunks.push(chunk);
132
+ }
133
+ return Buffer.concat(chunks).toString("utf8");
134
+ }