openmausbot 0.1.76 → 0.1.78

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 (64) hide show
  1. package/dist/assets/{index-C6blGSq5.js → index-BqDJqf2C.js} +1 -1
  2. package/dist/assets/index-DjwAroIZ.js +305 -0
  3. package/dist/assets/index-Pbb6Ao0s.css +1 -0
  4. package/dist/index.html +2 -2
  5. package/dist-server/container-mcp.js +5 -1
  6. package/dist-server/drivers/agents-proxy.js +838 -30
  7. package/dist-server/enterprise/server/index.js +387 -29
  8. package/dist-server/index.js +4097 -2063
  9. package/dist-server/openmausbot.js +730 -523
  10. package/dist-server/pair-cli.js +730 -523
  11. package/dist-server/server/agent-tool-policy.js +1 -0
  12. package/dist-server/server/auto-approve.js +16 -0
  13. package/dist-server/server/bot-overview.js +6 -2
  14. package/dist-server/server/bot-package.js +17 -3
  15. package/dist-server/server/browser-live.js +3 -0
  16. package/dist-server/server/chief-of-staff.js +2 -2
  17. package/dist-server/server/config.js +16 -0
  18. package/dist-server/server/drivers/acp/core.js +3 -1
  19. package/dist-server/server/drivers/agents-proxy.js +132 -27
  20. package/dist-server/server/drivers/boxagent.js +1 -1
  21. package/dist-server/server/drivers/claude.js +3 -2
  22. package/dist-server/server/drivers/codex.js +3 -1
  23. package/dist-server/server/drivers/pi.js +4 -0
  24. package/dist-server/server/enterprise.js +38 -0
  25. package/dist-server/server/environment.js +3 -0
  26. package/dist-server/server/fleet-agent.js +57 -13
  27. package/dist-server/server/fleet-cli.js +248 -31
  28. package/dist-server/server/fleet.js +88 -14
  29. package/dist-server/server/hosted-contract.js +16 -0
  30. package/dist-server/server/index.js +877 -197
  31. package/dist-server/server/message-file.js +4 -0
  32. package/dist-server/server/package-export.js +16 -14
  33. package/dist-server/server/peer-approval.js +1 -1
  34. package/dist-server/server/profile-requests.js +26 -4
  35. package/dist-server/server/provider-key-check.js +18 -1
  36. package/dist-server/server/request-auth.js +14 -5
  37. package/dist-server/server/room-handoffs.js +38 -1
  38. package/dist-server/server/routine-requests.js +86 -3
  39. package/dist-server/server/routines.js +31 -13
  40. package/dist-server/server/sessions.js +115 -8
  41. package/dist-server/server/setup-mode.js +11 -13
  42. package/dist-server/server/shared-computer-control.js +24 -0
  43. package/dist-server/server/shared-computers.js +131 -0
  44. package/dist-server/server/skill-learn.js +4 -4
  45. package/dist-server/server/skills.js +21 -0
  46. package/dist-server/server/steer-queue.js +2 -0
  47. package/dist-server/server/store.js +110 -8
  48. package/dist-server/server/system-prompt.js +7 -5
  49. package/dist-server/server/team-backup.js +6 -2
  50. package/dist-server/server/team-computers.js +108 -0
  51. package/dist-server/server/team-setup-requests.js +83 -31
  52. package/dist-server/server/tool-summary.js +53 -1
  53. package/dist-server/server/workspace-backup.js +1 -0
  54. package/dist-server/shared/approval-mode.js +4 -1
  55. package/dist-server/shared/cron-label.js +39 -0
  56. package/dist-server/shared/routine-schedule.js +89 -0
  57. package/dist-server/shared/team-backup.js +1 -1
  58. package/dist-server/shared/team-computer.js +1 -0
  59. package/dist-server/vps-container-mcp.js +5 -1
  60. package/enterprise/README.md +21 -3
  61. package/enterprise/server/index.js +387 -29
  62. package/package.json +1 -1
  63. package/dist/assets/index-BtT047dk.css +0 -1
  64. package/dist/assets/index-fKXFpB_J.js +0 -305
@@ -513,7 +513,7 @@ __export(core_exports2, {
513
513
  parse: () => parse,
514
514
  parseAsync: () => parseAsync,
515
515
  prettifyError: () => prettifyError,
516
- process: () => process,
516
+ process: () => process2,
517
517
  regexes: () => regexes_exports,
518
518
  registry: () => registry,
519
519
  safeDecode: () => safeDecode,
@@ -11438,7 +11438,7 @@ function initializeContext(params) {
11438
11438
  external: params?.external ?? void 0
11439
11439
  };
11440
11440
  }
11441
- function process(schema, ctx, _params = { path: [], schemaPath: [] }) {
11441
+ function process2(schema, ctx, _params = { path: [], schemaPath: [] }) {
11442
11442
  var _a3;
11443
11443
  const def = schema._zod.def;
11444
11444
  const seen = ctx.seen.get(schema);
@@ -11475,7 +11475,7 @@ function process(schema, ctx, _params = { path: [], schemaPath: [] }) {
11475
11475
  if (parent) {
11476
11476
  if (!result.ref)
11477
11477
  result.ref = parent;
11478
- process(parent, ctx, params);
11478
+ process2(parent, ctx, params);
11479
11479
  ctx.seen.get(parent).isParent = true;
11480
11480
  }
11481
11481
  }
@@ -11763,14 +11763,14 @@ function isTransforming(_schema, _ctx) {
11763
11763
  }
11764
11764
  var createToJSONSchemaMethod = (schema, processors = {}) => (params) => {
11765
11765
  const ctx = initializeContext({ ...params, processors });
11766
- process(schema, ctx);
11766
+ process2(schema, ctx);
11767
11767
  extractDefs(ctx, schema);
11768
11768
  return finalize(ctx, schema);
11769
11769
  };
11770
11770
  var createStandardJSONSchemaMethod = (schema, io, processors = {}) => (params) => {
11771
11771
  const { libraryOptions, target } = params ?? {};
11772
11772
  const ctx = initializeContext({ ...libraryOptions ?? {}, target, io, processors });
11773
- process(schema, ctx);
11773
+ process2(schema, ctx);
11774
11774
  extractDefs(ctx, schema);
11775
11775
  return finalize(ctx, schema);
11776
11776
  };
@@ -12016,7 +12016,7 @@ var arrayProcessor = (schema, ctx, _json, params) => {
12016
12016
  if (typeof maximum === "number")
12017
12017
  json2.maxItems = maximum;
12018
12018
  json2.type = "array";
12019
- json2.items = process(def.element, ctx, {
12019
+ json2.items = process2(def.element, ctx, {
12020
12020
  ...params,
12021
12021
  path: [...params.path, "items"]
12022
12022
  });
@@ -12028,7 +12028,7 @@ var objectProcessor = (schema, ctx, _json, params) => {
12028
12028
  json2.properties = {};
12029
12029
  const shape = def.shape;
12030
12030
  for (const key in shape) {
12031
- json2.properties[key] = process(shape[key], ctx, {
12031
+ json2.properties[key] = process2(shape[key], ctx, {
12032
12032
  ...params,
12033
12033
  path: [...params.path, "properties", key]
12034
12034
  });
@@ -12051,7 +12051,7 @@ var objectProcessor = (schema, ctx, _json, params) => {
12051
12051
  if (ctx.io === "output")
12052
12052
  json2.additionalProperties = false;
12053
12053
  } else if (def.catchall) {
12054
- json2.additionalProperties = process(def.catchall, ctx, {
12054
+ json2.additionalProperties = process2(def.catchall, ctx, {
12055
12055
  ...params,
12056
12056
  path: [...params.path, "additionalProperties"]
12057
12057
  });
@@ -12060,7 +12060,7 @@ var objectProcessor = (schema, ctx, _json, params) => {
12060
12060
  var unionProcessor = (schema, ctx, json2, params) => {
12061
12061
  const def = schema._zod.def;
12062
12062
  const isExclusive = def.inclusive === false;
12063
- const options = def.options.map((x, i) => process(x, ctx, {
12063
+ const options = def.options.map((x, i) => process2(x, ctx, {
12064
12064
  ...params,
12065
12065
  path: [...params.path, isExclusive ? "oneOf" : "anyOf", i]
12066
12066
  }));
@@ -12072,11 +12072,11 @@ var unionProcessor = (schema, ctx, json2, params) => {
12072
12072
  };
12073
12073
  var intersectionProcessor = (schema, ctx, json2, params) => {
12074
12074
  const def = schema._zod.def;
12075
- const a = process(def.left, ctx, {
12075
+ const a = process2(def.left, ctx, {
12076
12076
  ...params,
12077
12077
  path: [...params.path, "allOf", 0]
12078
12078
  });
12079
- const b = process(def.right, ctx, {
12079
+ const b = process2(def.right, ctx, {
12080
12080
  ...params,
12081
12081
  path: [...params.path, "allOf", 1]
12082
12082
  });
@@ -12093,11 +12093,11 @@ var tupleProcessor = (schema, ctx, _json, params) => {
12093
12093
  json2.type = "array";
12094
12094
  const prefixPath = ctx.target === "draft-2020-12" ? "prefixItems" : "items";
12095
12095
  const restPath = ctx.target === "draft-2020-12" ? "items" : ctx.target === "openapi-3.0" ? "items" : "additionalItems";
12096
- const prefixItems = def.items.map((x, i) => process(x, ctx, {
12096
+ const prefixItems = def.items.map((x, i) => process2(x, ctx, {
12097
12097
  ...params,
12098
12098
  path: [...params.path, prefixPath, i]
12099
12099
  }));
12100
- const rest = def.rest ? process(def.rest, ctx, {
12100
+ const rest = def.rest ? process2(def.rest, ctx, {
12101
12101
  ...params,
12102
12102
  path: [...params.path, restPath, ...ctx.target === "openapi-3.0" ? [def.items.length] : []]
12103
12103
  }) : null;
@@ -12137,7 +12137,7 @@ var recordProcessor = (schema, ctx, _json, params) => {
12137
12137
  const keyBag = keyType._zod.bag;
12138
12138
  const patterns = keyBag?.patterns;
12139
12139
  if (def.mode === "loose" && patterns && patterns.size > 0) {
12140
- const valueSchema = process(def.valueType, ctx, {
12140
+ const valueSchema = process2(def.valueType, ctx, {
12141
12141
  ...params,
12142
12142
  path: [...params.path, "patternProperties", "*"]
12143
12143
  });
@@ -12147,12 +12147,12 @@ var recordProcessor = (schema, ctx, _json, params) => {
12147
12147
  }
12148
12148
  } else {
12149
12149
  if (ctx.target === "draft-07" || ctx.target === "draft-2020-12") {
12150
- json2.propertyNames = process(def.keyType, ctx, {
12150
+ json2.propertyNames = process2(def.keyType, ctx, {
12151
12151
  ...params,
12152
12152
  path: [...params.path, "propertyNames"]
12153
12153
  });
12154
12154
  }
12155
- json2.additionalProperties = process(def.valueType, ctx, {
12155
+ json2.additionalProperties = process2(def.valueType, ctx, {
12156
12156
  ...params,
12157
12157
  path: [...params.path, "additionalProperties"]
12158
12158
  });
@@ -12167,7 +12167,7 @@ var recordProcessor = (schema, ctx, _json, params) => {
12167
12167
  };
12168
12168
  var nullableProcessor = (schema, ctx, json2, params) => {
12169
12169
  const def = schema._zod.def;
12170
- const inner = process(def.innerType, ctx, params);
12170
+ const inner = process2(def.innerType, ctx, params);
12171
12171
  const seen = ctx.seen.get(schema);
12172
12172
  if (ctx.target === "openapi-3.0") {
12173
12173
  seen.ref = def.innerType;
@@ -12178,20 +12178,20 @@ var nullableProcessor = (schema, ctx, json2, params) => {
12178
12178
  };
12179
12179
  var nonoptionalProcessor = (schema, ctx, _json, params) => {
12180
12180
  const def = schema._zod.def;
12181
- process(def.innerType, ctx, params);
12181
+ process2(def.innerType, ctx, params);
12182
12182
  const seen = ctx.seen.get(schema);
12183
12183
  seen.ref = def.innerType;
12184
12184
  };
12185
12185
  var defaultProcessor = (schema, ctx, json2, params) => {
12186
12186
  const def = schema._zod.def;
12187
- process(def.innerType, ctx, params);
12187
+ process2(def.innerType, ctx, params);
12188
12188
  const seen = ctx.seen.get(schema);
12189
12189
  seen.ref = def.innerType;
12190
12190
  json2.default = JSON.parse(JSON.stringify(def.defaultValue));
12191
12191
  };
12192
12192
  var prefaultProcessor = (schema, ctx, json2, params) => {
12193
12193
  const def = schema._zod.def;
12194
- process(def.innerType, ctx, params);
12194
+ process2(def.innerType, ctx, params);
12195
12195
  const seen = ctx.seen.get(schema);
12196
12196
  seen.ref = def.innerType;
12197
12197
  if (ctx.io === "input")
@@ -12199,7 +12199,7 @@ var prefaultProcessor = (schema, ctx, json2, params) => {
12199
12199
  };
12200
12200
  var catchProcessor = (schema, ctx, json2, params) => {
12201
12201
  const def = schema._zod.def;
12202
- process(def.innerType, ctx, params);
12202
+ process2(def.innerType, ctx, params);
12203
12203
  const seen = ctx.seen.get(schema);
12204
12204
  seen.ref = def.innerType;
12205
12205
  let catchValue;
@@ -12214,32 +12214,32 @@ var pipeProcessor = (schema, ctx, _json, params) => {
12214
12214
  const def = schema._zod.def;
12215
12215
  const inIsTransform = def.in._zod.traits.has("$ZodTransform");
12216
12216
  const innerType = ctx.io === "input" ? inIsTransform ? def.out : def.in : def.out;
12217
- process(innerType, ctx, params);
12217
+ process2(innerType, ctx, params);
12218
12218
  const seen = ctx.seen.get(schema);
12219
12219
  seen.ref = innerType;
12220
12220
  };
12221
12221
  var readonlyProcessor = (schema, ctx, json2, params) => {
12222
12222
  const def = schema._zod.def;
12223
- process(def.innerType, ctx, params);
12223
+ process2(def.innerType, ctx, params);
12224
12224
  const seen = ctx.seen.get(schema);
12225
12225
  seen.ref = def.innerType;
12226
12226
  json2.readOnly = true;
12227
12227
  };
12228
12228
  var promiseProcessor = (schema, ctx, _json, params) => {
12229
12229
  const def = schema._zod.def;
12230
- process(def.innerType, ctx, params);
12230
+ process2(def.innerType, ctx, params);
12231
12231
  const seen = ctx.seen.get(schema);
12232
12232
  seen.ref = def.innerType;
12233
12233
  };
12234
12234
  var optionalProcessor = (schema, ctx, _json, params) => {
12235
12235
  const def = schema._zod.def;
12236
- process(def.innerType, ctx, params);
12236
+ process2(def.innerType, ctx, params);
12237
12237
  const seen = ctx.seen.get(schema);
12238
12238
  seen.ref = def.innerType;
12239
12239
  };
12240
12240
  var lazyProcessor = (schema, ctx, _json, params) => {
12241
12241
  const innerType = schema._zod.innerType;
12242
- process(innerType, ctx, params);
12242
+ process2(innerType, ctx, params);
12243
12243
  const seen = ctx.seen.get(schema);
12244
12244
  seen.ref = innerType;
12245
12245
  };
@@ -12291,7 +12291,7 @@ function toJSONSchema(input, params) {
12291
12291
  const defs = {};
12292
12292
  for (const entry of registry2._idmap.entries()) {
12293
12293
  const [_, schema] = entry;
12294
- process(schema, ctx2);
12294
+ process2(schema, ctx2);
12295
12295
  }
12296
12296
  const schemas = {};
12297
12297
  const external = {
@@ -12314,7 +12314,7 @@ function toJSONSchema(input, params) {
12314
12314
  return { schemas };
12315
12315
  }
12316
12316
  const ctx = initializeContext({ ...params, processors: allProcessors });
12317
- process(input, ctx);
12317
+ process2(input, ctx);
12318
12318
  extractDefs(ctx, input);
12319
12319
  return finalize(ctx, input);
12320
12320
  }
@@ -12372,7 +12372,7 @@ var JSONSchemaGenerator = class {
12372
12372
  * This must be called before emit().
12373
12373
  */
12374
12374
  process(schema, _params = { path: [], schemaPath: [] }) {
12375
- return process(schema, this.ctx, _params);
12375
+ return process2(schema, this.ctx, _params);
12376
12376
  }
12377
12377
  /**
12378
12378
  * Emit the final JSON Schema after processing.
@@ -14576,6 +14576,363 @@ function verifyLicenseKey(key, options = {}) {
14576
14576
  return claims;
14577
14577
  }
14578
14578
 
14579
+ // enterprise/server/workspace-access.ts
14580
+ import { createHash, randomBytes, timingSafeEqual } from "node:crypto";
14581
+
14582
+ // server/enterprise.ts
14583
+ import { join as join2 } from "node:path";
14584
+
14585
+ // server/proxy-paths.ts
14586
+ import { existsSync } from "node:fs";
14587
+ import { dirname, join } from "node:path";
14588
+ import { fileURLToPath } from "node:url";
14589
+ var SERVER_ROOT = dirname(fileURLToPath(import.meta.url));
14590
+ function resolveProxy(relative) {
14591
+ const source = join(SERVER_ROOT, `${relative}.ts`);
14592
+ return existsSync(source) ? source : join(SERVER_ROOT, `${relative}.js`);
14593
+ }
14594
+ var SPAWNED_PROXIES = {
14595
+ browser: resolveProxy("browser-proxy"),
14596
+ computer: resolveProxy("computer-proxy"),
14597
+ localComputer: resolveProxy("local-computer-proxy"),
14598
+ permission: resolveProxy("permission-proxy"),
14599
+ containerMcp: resolveProxy("container-mcp"),
14600
+ vpsContainerMcp: resolveProxy("vps-container-mcp"),
14601
+ agents: resolveProxy("drivers/agents-proxy"),
14602
+ dweb: resolveProxy("drivers/dweb-proxy"),
14603
+ connectors: resolveProxy("connector-proxy"),
14604
+ mcpGate: resolveProxy("mcp-gate"),
14605
+ phone: resolveProxy("drivers/phone-proxy"),
14606
+ // Loaded by the external `pi` process via `-e`, not by this server — but
14607
+ // resolved through the same single source of truth so the packaged layout
14608
+ // check can assert it ships.
14609
+ piMcpExtension: resolveProxy("drivers/pi-mcp-extension")
14610
+ };
14611
+
14612
+ // server/enterprise.ts
14613
+ var layerSchema = external_exports.object({
14614
+ customer: external_exports.string().min(1),
14615
+ features: external_exports.array(external_exports.string().min(1)),
14616
+ expiresAt: external_exports.string().nullable()
14617
+ });
14618
+ var DEFAULT_DIR = join2(SERVER_ROOT, "..", "enterprise");
14619
+ function hostedWorkspaceConfiguration(env = process.env) {
14620
+ try {
14621
+ const origin = (raw) => {
14622
+ if (!raw || raw !== raw.trim()) throw new Error("missing origin");
14623
+ const url2 = new URL(raw);
14624
+ if (url2.protocol !== "https:" || raw !== url2.origin && raw !== `${url2.origin}/`) throw new Error("invalid origin");
14625
+ return url2;
14626
+ };
14627
+ if (!/^[a-z][a-z0-9-]{1,30}$/.test(env.OMB_ADMIN_WORKSPACE ?? "")) return null;
14628
+ if (env.OMB_ADMIN_MEMBERSHIP !== void 0 && !["local", "portal"].includes(env.OMB_ADMIN_MEMBERSHIP)) return null;
14629
+ return { admin: origin(env.OMB_ADMIN_URL), tenant: origin(env.OMB_PUBLIC_URL), workspace: env.OMB_ADMIN_WORKSPACE, portalMembership: env.OMB_ADMIN_MEMBERSHIP === "portal" };
14630
+ } catch {
14631
+ return null;
14632
+ }
14633
+ }
14634
+
14635
+ // server/request-auth.ts
14636
+ import { isIP } from "node:net";
14637
+ function isLoopbackHost(host) {
14638
+ if (!host) return false;
14639
+ const value = host.trim().toLowerCase();
14640
+ if (!value) return false;
14641
+ let hostname3 = value;
14642
+ if (value.startsWith("[")) {
14643
+ const close = value.indexOf("]");
14644
+ if (close < 0 || value.length > close + 1 && !/^:\d+$/.test(value.slice(close + 1))) return false;
14645
+ hostname3 = value.slice(1, close);
14646
+ } else {
14647
+ const firstColon = value.indexOf(":");
14648
+ const lastColon = value.lastIndexOf(":");
14649
+ if (firstColon >= 0 && firstColon === lastColon) {
14650
+ if (!/^\d+$/.test(value.slice(firstColon + 1))) return false;
14651
+ hostname3 = value.slice(0, firstColon);
14652
+ }
14653
+ }
14654
+ if (hostname3 === "localhost" || hostname3 === "localhost.") return true;
14655
+ if (isIP(hostname3) === 4) return hostname3.startsWith("127.");
14656
+ return hostname3 === "::1" || hostname3 === "0:0:0:0:0:0:0:1";
14657
+ }
14658
+ function isAllowedOrigin(origin) {
14659
+ if (!origin) return true;
14660
+ try {
14661
+ const o = new URL(origin);
14662
+ return isLoopbackHost(o.hostname) && (o.protocol === "http:" || o.protocol === "https:");
14663
+ } catch {
14664
+ return false;
14665
+ }
14666
+ }
14667
+ function headerValue(value) {
14668
+ return Array.isArray(value) ? value[0] : value;
14669
+ }
14670
+ function isProxied(req) {
14671
+ const h = req.headers;
14672
+ return ipcPeer(req) || Boolean(h["x-forwarded-for"] || h["x-forwarded-proto"] || h["x-forwarded-host"] || h["forwarded"]);
14673
+ }
14674
+ function ipcPeer(req) {
14675
+ const socket = req.socket;
14676
+ return Boolean(socket) && socket.remoteAddress === void 0 && socket.remoteFamily === void 0;
14677
+ }
14678
+ function requestSource(req) {
14679
+ const peer = req.socket?.remoteAddress || "unknown";
14680
+ const viaLocalProxy = ipcPeer(req) || peer === "127.0.0.1" || peer === "::1" || peer === "::ffff:127.0.0.1";
14681
+ const hops = viaLocalProxy ? (headerValue(req.headers["x-forwarded-for"]) ?? "").split(",").map((h) => h.trim()).filter(Boolean) : [];
14682
+ const forwarded = hops.length ? hops[hops.length - 1] : void 0;
14683
+ return sanitizeSource(forwarded || peer);
14684
+ }
14685
+ function sanitizeSource(value) {
14686
+ const clean = value.replace(/[^\w.:%[\]-]/g, "");
14687
+ return (clean || "unknown").slice(0, 64);
14688
+ }
14689
+ function parseCookies(header) {
14690
+ const out = /* @__PURE__ */ new Map();
14691
+ if (!header) return out;
14692
+ for (const part of header.split(";")) {
14693
+ const eq = part.indexOf("=");
14694
+ if (eq < 0) continue;
14695
+ const name = part.slice(0, eq).trim();
14696
+ if (!name) continue;
14697
+ out.set(name, part.slice(eq + 1).trim());
14698
+ }
14699
+ return out;
14700
+ }
14701
+ function serializeSessionCookie(name, token, options) {
14702
+ const parts = [`${name}=${token}`, "Path=/", "HttpOnly", "SameSite=Lax", `Max-Age=${options.maxAgeSeconds}`];
14703
+ if (options.secure) parts.push("Secure");
14704
+ return parts.join("; ");
14705
+ }
14706
+
14707
+ // server/sessions.ts
14708
+ var PAIRING_CODE_TTL_MS = 5 * 6e4;
14709
+ function daysMs(value, fallbackDays) {
14710
+ const days = Number(value);
14711
+ const ok = Number.isInteger(days) && days >= 1 && days <= 3650;
14712
+ return (ok ? days : fallbackDays) * 24 * 60 * 6e4;
14713
+ }
14714
+ var SESSION_TTL_MS = daysMs(process.env.OMB_SESSION_TTL_DAYS, 30);
14715
+ var SESSION_MAX_AGE_MS = daysMs(process.env.OMB_SESSION_MAX_DAYS, 180);
14716
+ var SESSION_RENEW_WHEN_LEFT_MS = SESSION_TTL_MS / 2;
14717
+ function cookieMaxAgeSeconds(session, now = Date.now()) {
14718
+ return Math.max(1, Math.floor((session.expiresAt - now) / 1e3));
14719
+ }
14720
+ var STREAM_TICKET_TTL_MS = 5 * 6e4;
14721
+ var scopeSchema = external_exports.enum(["admin", "client"]);
14722
+ var sessionSchema = external_exports.object({
14723
+ id: external_exports.string().min(1),
14724
+ tokenHash: external_exports.string().length(64),
14725
+ label: external_exports.string().max(80),
14726
+ scopes: external_exports.array(scopeSchema).min(1),
14727
+ createdAt: external_exports.number(),
14728
+ lastSeenAt: external_exports.number(),
14729
+ expiresAt: external_exports.number(),
14730
+ /** Set when the session came from an account sign-in rather than a code. */
14731
+ userId: external_exports.string().max(256).optional(),
14732
+ email: external_exports.string().max(320).optional(),
14733
+ /** Set only by the internal verified-portal issuance path, never by email or pairing input. */
14734
+ membershipAuthority: external_exports.literal("portal").optional()
14735
+ });
14736
+ var fileSchema = external_exports.object({ version: external_exports.literal(1), sessions: external_exports.array(sessionSchema) });
14737
+
14738
+ // server/hosted-contract.ts
14739
+ var HOSTED_CONTRACT_VERSION = 1;
14740
+ var HOSTED_CONTRACT_METADATA = Object.freeze({
14741
+ contractVersion: HOSTED_CONTRACT_VERSION,
14742
+ supportedContractVersions: Object.freeze([HOSTED_CONTRACT_VERSION]),
14743
+ legacyPolicy: "legacy-v1"
14744
+ });
14745
+ var HOSTED_CONTRACT_ERROR = "Admin and workspace runtime versions are incompatible. Ask your administrator to update the deployment.";
14746
+ function hostedContractCompatible(version2) {
14747
+ return version2 === void 0 ? HOSTED_CONTRACT_VERSION === 1 : version2 === HOSTED_CONTRACT_VERSION;
14748
+ }
14749
+
14750
+ // enterprise/server/workspace-access.ts
14751
+ var START = "/api/auth/hosted/start";
14752
+ var CALLBACK = "/api/auth/hosted/callback";
14753
+ var TTL_MS = 3e5;
14754
+ var MAX_PENDING = 1e3;
14755
+ var START_WINDOW_MS = 6e4;
14756
+ var STARTS_PER_SOURCE = 20;
14757
+ var PROOF = /^[A-Za-z0-9_-]{43}$/;
14758
+ var unavailable = { status: 503, error: "Workspace sign-in is unavailable. Try again shortly." };
14759
+ var denied = { status: 401, error: "Workspace access ended. Sign in again." };
14760
+ var incompatible = { status: 503, error: HOSTED_CONTRACT_ERROR };
14761
+ function equal(a, b) {
14762
+ const x = Buffer.from(a), y = Buffer.from(b);
14763
+ return x.length === y.length && timingSafeEqual(x, y);
14764
+ }
14765
+ function identity(value) {
14766
+ if (!value || typeof value !== "object") return null;
14767
+ const { email: email3, role } = value;
14768
+ if (typeof email3 !== "string" || email3.length > 320 || !/^[^@\s]+@[^@\s]+\.[^@\s]+$/.test(email3)) return null;
14769
+ if (role !== "admin" && role !== "member") return null;
14770
+ return { email: email3.toLowerCase(), scopes: role === "admin" ? ["admin", "client"] : ["client"] };
14771
+ }
14772
+ function createWorkspaceAccess(options) {
14773
+ const env = options.env ?? process.env;
14774
+ const now = options.now ?? Date.now;
14775
+ const fetchImpl = options.fetchImpl ?? fetch;
14776
+ const config2 = hostedWorkspaceConfiguration(env);
14777
+ const cookieName = `__Host-${options.cookieName}_handoff`;
14778
+ const pending = /* @__PURE__ */ new Map();
14779
+ const starts = /* @__PURE__ */ new Map();
14780
+ const observed = /* @__PURE__ */ new Map();
14781
+ let checking = false;
14782
+ const clearCookie = () => serializeSessionCookie(cookieName, "", { secure: true, maxAgeSeconds: 0 });
14783
+ const tenantHost = (req) => config2 !== null && req.headers.host?.toLowerCase() === config2.tenant.host;
14784
+ const localOwner = (req) => !isProxied(req) && isLoopbackHost(req.headers.host) && isAllowedOrigin(typeof req.headers.origin === "string" ? req.headers.origin : void 0);
14785
+ const redirect = (res, to) => {
14786
+ res.writeHead(302, { location: to, "cache-control": "no-store", "referrer-policy": "no-referrer" });
14787
+ res.end();
14788
+ };
14789
+ const failurePage = (res, status, message = "Workspace sign-in could not finish.") => {
14790
+ res.setHeader("set-cookie", clearCookie());
14791
+ res.writeHead(status, { "content-type": "text/html; charset=utf-8", "cache-control": "no-store", "referrer-policy": "no-referrer" });
14792
+ res.end(`<!doctype html><title>Workspace sign-in</title><p>${message}</p><a href="${START}">Try signing in again</a>`);
14793
+ };
14794
+ const post = async (path, body) => {
14795
+ if (!config2 || !options.entitled()) return { status: 503, body: null };
14796
+ try {
14797
+ const response = await fetchImpl(new URL(path, config2.admin), {
14798
+ method: "POST",
14799
+ headers: { "content-type": "application/json" },
14800
+ body: JSON.stringify({ workspace: config2.workspace, ...body, contractVersion: HOSTED_CONTRACT_VERSION }),
14801
+ credentials: "omit",
14802
+ redirect: "error",
14803
+ signal: AbortSignal.timeout(5e3)
14804
+ });
14805
+ const responseBody = response.ok ? await response.json() : null;
14806
+ if (response.ok && responseBody && typeof responseBody === "object" && !hostedContractCompatible(Reflect.get(responseBody, "contractVersion"))) return { status: 409, body: null };
14807
+ return options.entitled() ? { status: response.status, body: responseBody } : { status: 503, body: null };
14808
+ } catch {
14809
+ return { status: 503, body: null };
14810
+ }
14811
+ };
14812
+ const check2 = async (session) => {
14813
+ if (!options.sessions.isLive(session.id)) {
14814
+ observed.delete(session.id);
14815
+ return denied;
14816
+ }
14817
+ const result = await post("/api/handoff/check", { grant: session.userId.slice("portal:".length) });
14818
+ if (result.status === 409) {
14819
+ options.closeSessionStreams(session.id);
14820
+ return incompatible;
14821
+ }
14822
+ if (result.status !== 200 && result.status !== 401) {
14823
+ options.closeSessionStreams(session.id);
14824
+ return unavailable;
14825
+ }
14826
+ const current = identity(result.body);
14827
+ if (!current || current.email !== session.email?.toLowerCase() || session.scopes.some((scope) => !current.scopes.includes(scope))) {
14828
+ options.sessions.revoke(session.id);
14829
+ observed.delete(session.id);
14830
+ return denied;
14831
+ }
14832
+ return options.sessions.isLive(session.id) ? null : denied;
14833
+ };
14834
+ const access = {
14835
+ async handlePublic(req, res, url2) {
14836
+ const path = url2.pathname;
14837
+ if (req.method !== "GET" || !["/", "/pair", START, CALLBACK].includes(path)) return false;
14838
+ if ((path === "/" || path === "/pair") && localOwner(req)) return false;
14839
+ if (!config2 || !options.entitled()) {
14840
+ failurePage(res, 503);
14841
+ return true;
14842
+ }
14843
+ if (!tenantHost(req)) {
14844
+ failurePage(res, 403);
14845
+ return true;
14846
+ }
14847
+ if (path === "/pair") {
14848
+ redirect(res, START);
14849
+ return true;
14850
+ }
14851
+ if (path === "/") {
14852
+ const token = parseCookies(req.headers.cookie).get(options.cookieName);
14853
+ const session = options.sessions.authenticate(token);
14854
+ if (session) {
14855
+ const failure = await access.authorize(req, { kind: "session", session, scopes: session.scopes, via: "cookie" });
14856
+ if (!failure) return false;
14857
+ if (failure.status === 503) {
14858
+ failurePage(res, 503, failure.error);
14859
+ return true;
14860
+ }
14861
+ }
14862
+ redirect(res, START);
14863
+ return true;
14864
+ }
14865
+ if (path === START) {
14866
+ const time3 = now();
14867
+ for (const [state3, value] of pending) if (value.expiresAt <= time3) pending.delete(state3);
14868
+ for (const [source2, value] of starts) if (value.expiresAt <= time3) starts.delete(source2);
14869
+ const source = requestSource(req), recent = starts.get(source);
14870
+ if (pending.size >= MAX_PENDING || (recent?.count ?? 0) >= STARTS_PER_SOURCE || !recent && starts.size >= MAX_PENDING) {
14871
+ res.writeHead(429, { "content-type": "text/html; charset=utf-8", "cache-control": "no-store", "referrer-policy": "no-referrer", "retry-after": String(START_WINDOW_MS / 1e3) });
14872
+ res.end("<!doctype html><title>Workspace sign-in</title><p>Too many sign-in attempts. Try again shortly.</p>");
14873
+ return true;
14874
+ }
14875
+ starts.set(source, { count: (recent?.count ?? 0) + 1, expiresAt: recent?.expiresAt ?? time3 + START_WINDOW_MS });
14876
+ const state2 = randomBytes(32).toString("base64url");
14877
+ const verifier = randomBytes(32).toString("base64url");
14878
+ pending.set(state2, { verifier, expiresAt: time3 + TTL_MS });
14879
+ res.setHeader("set-cookie", serializeSessionCookie(cookieName, `${state2}.${verifier}`, { secure: true, maxAgeSeconds: TTL_MS / 1e3 }));
14880
+ const target = new URL("/connect", config2.admin);
14881
+ target.search = new URLSearchParams({ workspace: config2.workspace, state: state2, challenge: createHash("sha256").update(verifier).digest("base64url") }).toString();
14882
+ redirect(res, target.href);
14883
+ return true;
14884
+ }
14885
+ const state = url2.searchParams.get("state") ?? "";
14886
+ const code = url2.searchParams.get("code") ?? "";
14887
+ const saved = pending.get(state);
14888
+ const cookie = parseCookies(req.headers.cookie).get(cookieName) ?? "";
14889
+ if (!saved || saved.expiresAt <= now() || !equal(cookie, `${state}.${saved.verifier}`) || !PROOF.test(code) || url2.searchParams.getAll("state").length !== 1 || url2.searchParams.getAll("code").length !== 1) {
14890
+ failurePage(res, 400);
14891
+ return true;
14892
+ }
14893
+ pending.delete(state);
14894
+ res.setHeader("set-cookie", clearCookie());
14895
+ const result = await post("/api/handoff/consume", { code, verifier: saved.verifier });
14896
+ if (result.status === 409) {
14897
+ failurePage(res, 503, incompatible.error);
14898
+ return true;
14899
+ }
14900
+ const user = identity(result.body);
14901
+ const grant = result.body && typeof result.body === "object" ? Reflect.get(result.body, "grant") : null;
14902
+ if (result.status !== 200 || !user || typeof grant !== "string" || !PROOF.test(grant)) {
14903
+ failurePage(res, result.status === 401 ? 401 : 503);
14904
+ return true;
14905
+ }
14906
+ const issued = options.sessions.issuePortal({ email: user.email, grant, scopes: user.scopes });
14907
+ if (!options.sessions.authenticate(issued.token)) {
14908
+ failurePage(res, 403);
14909
+ return true;
14910
+ }
14911
+ res.setHeader("set-cookie", [clearCookie(), serializeSessionCookie(options.cookieName, issued.token, { secure: true, maxAgeSeconds: cookieMaxAgeSeconds(issued.session) })]);
14912
+ redirect(res, "/");
14913
+ return true;
14914
+ },
14915
+ async authorize(req, auth) {
14916
+ if (auth.kind === "loopback") return null;
14917
+ if (!config2 || !options.entitled()) return unavailable;
14918
+ if (!tenantHost(req)) return { status: 403, error: "Use the configured workspace address." };
14919
+ if (!auth.session.userId?.startsWith("portal:") || auth.session.userId.length <= "portal:".length) return denied;
14920
+ observed.set(auth.session.id, auth.session);
14921
+ return check2(auth.session);
14922
+ },
14923
+ async revalidate() {
14924
+ if (checking) return;
14925
+ checking = true;
14926
+ try {
14927
+ await Promise.all([...observed.values()].map(check2));
14928
+ } finally {
14929
+ checking = false;
14930
+ }
14931
+ }
14932
+ };
14933
+ return access;
14934
+ }
14935
+
14579
14936
  // enterprise/server/index.ts
14580
14937
  function register(options) {
14581
14938
  if (!options.licenseKey) return null;
@@ -14583,5 +14940,6 @@ function register(options) {
14583
14940
  return { customer: claims.customer, features: claims.features, expiresAt: claims.expires };
14584
14941
  }
14585
14942
  export {
14943
+ createWorkspaceAccess,
14586
14944
  register
14587
14945
  };