openmausbot 0.1.76 → 0.1.77
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/assets/{index-C6blGSq5.js → index-DeK-z9ld.js} +1 -1
- package/dist/assets/index-XdHJ-9CF.css +1 -0
- package/dist/assets/index-lhqE1umn.js +305 -0
- package/dist/index.html +2 -2
- package/dist-server/container-mcp.js +5 -1
- package/dist-server/drivers/agents-proxy.js +52 -1
- package/dist-server/enterprise/server/index.js +387 -29
- package/dist-server/index.js +1377 -459
- package/dist-server/openmausbot.js +725 -522
- package/dist-server/pair-cli.js +725 -522
- package/dist-server/server/agent-tool-policy.js +1 -0
- package/dist-server/server/browser-live.js +3 -0
- package/dist-server/server/config.js +16 -0
- package/dist-server/server/drivers/acp/core.js +3 -1
- package/dist-server/server/drivers/agents-proxy.js +58 -2
- package/dist-server/server/drivers/boxagent.js +1 -1
- package/dist-server/server/drivers/claude.js +3 -2
- package/dist-server/server/drivers/codex.js +3 -1
- package/dist-server/server/drivers/pi.js +4 -0
- package/dist-server/server/enterprise.js +38 -0
- package/dist-server/server/environment.js +3 -0
- package/dist-server/server/fleet-agent.js +57 -13
- package/dist-server/server/fleet-cli.js +248 -31
- package/dist-server/server/fleet.js +88 -14
- package/dist-server/server/hosted-contract.js +16 -0
- package/dist-server/server/index.js +671 -100
- package/dist-server/server/message-file.js +4 -0
- package/dist-server/server/provider-key-check.js +18 -1
- package/dist-server/server/request-auth.js +14 -5
- package/dist-server/server/sessions.js +115 -8
- package/dist-server/server/shared-computer-control.js +24 -0
- package/dist-server/server/shared-computers.js +131 -0
- package/dist-server/server/steer-queue.js +2 -0
- package/dist-server/server/store.js +20 -7
- package/dist-server/server/team-computers.js +108 -0
- package/dist-server/server/tool-summary.js +53 -1
- package/dist-server/server/workspace-backup.js +1 -0
- package/dist-server/shared/approval-mode.js +4 -1
- package/dist-server/shared/team-computer.js +1 -0
- package/dist-server/vps-container-mcp.js +5 -1
- package/enterprise/README.md +21 -3
- package/enterprise/server/index.js +387 -29
- package/package.json +1 -1
- package/dist/assets/index-BtT047dk.css +0 -1
- 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: () =>
|
|
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
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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 =
|
|
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] =
|
|
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 =
|
|
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) =>
|
|
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 =
|
|
12075
|
+
const a = process2(def.left, ctx, {
|
|
12076
12076
|
...params,
|
|
12077
12077
|
path: [...params.path, "allOf", 0]
|
|
12078
12078
|
});
|
|
12079
|
-
const b =
|
|
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) =>
|
|
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 ?
|
|
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 =
|
|
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 =
|
|
12150
|
+
json2.propertyNames = process2(def.keyType, ctx, {
|
|
12151
12151
|
...params,
|
|
12152
12152
|
path: [...params.path, "propertyNames"]
|
|
12153
12153
|
});
|
|
12154
12154
|
}
|
|
12155
|
-
json2.additionalProperties =
|
|
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 =
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
|
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
|
};
|