@objectstack/runtime 6.5.1 → 6.7.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/index.cjs +292 -16
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +65 -8
- package/dist/index.d.ts +65 -8
- package/dist/index.js +295 -20
- package/dist/index.js.map +1 -1
- package/package.json +28 -19
package/dist/index.cjs
CHANGED
|
@@ -2141,6 +2141,7 @@ __export(index_exports, {
|
|
|
2141
2141
|
resolveDefaultArtifactPath: () => resolveDefaultArtifactPath,
|
|
2142
2142
|
resolveErrorReporter: () => resolveErrorReporter,
|
|
2143
2143
|
resolveMetrics: () => resolveMetrics,
|
|
2144
|
+
resolveObjectStackHome: () => resolveObjectStackHome,
|
|
2144
2145
|
resolveRequestId: () => resolveRequestId,
|
|
2145
2146
|
seedPlatformSsoClient: () => seedPlatformSsoClient
|
|
2146
2147
|
});
|
|
@@ -2196,8 +2197,17 @@ var Runtime = class {
|
|
|
2196
2197
|
// src/standalone-stack.ts
|
|
2197
2198
|
var import_node_path2 = require("path");
|
|
2198
2199
|
var import_node_fs = require("fs");
|
|
2200
|
+
var import_node_os = require("os");
|
|
2199
2201
|
var import_zod = require("zod");
|
|
2200
2202
|
init_load_artifact_bundle();
|
|
2203
|
+
function resolveObjectStackHome() {
|
|
2204
|
+
const raw = process.env.OS_HOME?.trim();
|
|
2205
|
+
if (raw && raw.length > 0) {
|
|
2206
|
+
if (raw.startsWith("~")) return (0, import_node_path2.resolve)((0, import_node_os.homedir)(), raw.slice(1).replace(/^[/\\]/, ""));
|
|
2207
|
+
return (0, import_node_path2.resolve)(raw);
|
|
2208
|
+
}
|
|
2209
|
+
return (0, import_node_path2.resolve)((0, import_node_os.homedir)(), ".objectstack");
|
|
2210
|
+
}
|
|
2201
2211
|
var StandaloneStackConfigSchema = import_zod.z.object({
|
|
2202
2212
|
databaseUrl: import_zod.z.string().optional(),
|
|
2203
2213
|
databaseAuthToken: import_zod.z.string().optional(),
|
|
@@ -2228,7 +2238,7 @@ async function createStandaloneStack(config) {
|
|
|
2228
2238
|
const environmentId = cfg.environmentId ?? process.env.OS_ENVIRONMENT_ID ?? "proj_local";
|
|
2229
2239
|
const artifactPathInput = cfg.artifactPath ?? process.env.OS_ARTIFACT_PATH ?? (0, import_node_path2.resolve)(cwd, "dist/objectstack.json");
|
|
2230
2240
|
const artifactPath = isHttpUrl(artifactPathInput) ? artifactPathInput : artifactPathInput.startsWith("/") ? artifactPathInput : (0, import_node_path2.resolve)(cwd, artifactPathInput);
|
|
2231
|
-
const dbUrl = cfg.databaseUrl ?? process.env.OS_DATABASE_URL?.trim() ?? process.env.TURSO_DATABASE_URL?.trim() ?? `file:${(0, import_node_path2.resolve)(
|
|
2241
|
+
const dbUrl = cfg.databaseUrl ?? process.env.OS_DATABASE_URL?.trim() ?? process.env.TURSO_DATABASE_URL?.trim() ?? `file:${(0, import_node_path2.resolve)(resolveObjectStackHome(), "data/standalone.db")}`;
|
|
2232
2242
|
const dbAuthToken = cfg.databaseAuthToken ?? process.env.OS_DATABASE_AUTH_TOKEN?.trim() ?? process.env.TURSO_AUTH_TOKEN?.trim();
|
|
2233
2243
|
const explicitDriver = cfg.databaseDriver ?? process.env.OS_DATABASE_DRIVER?.trim();
|
|
2234
2244
|
const dbDriver = explicitDriver ?? detectDriverFromUrl(dbUrl);
|
|
@@ -2237,7 +2247,14 @@ async function createStandaloneStack(config) {
|
|
|
2237
2247
|
const { InMemoryDriver } = await import("@objectstack/driver-memory");
|
|
2238
2248
|
driverPlugin = new DriverPlugin2(new InMemoryDriver());
|
|
2239
2249
|
} else if (dbDriver === "turso") {
|
|
2240
|
-
|
|
2250
|
+
let TursoDriver;
|
|
2251
|
+
try {
|
|
2252
|
+
({ TursoDriver } = await import("@objectstack/driver-turso"));
|
|
2253
|
+
} catch (err) {
|
|
2254
|
+
throw new Error(
|
|
2255
|
+
`[StandaloneStack] libsql/turso URL detected ("${dbUrl}") but @objectstack/driver-turso is not installed. Install it with: npm install @objectstack/driver-turso (or use a file: URL to default to better-sqlite3). (${err?.message ?? err})`
|
|
2256
|
+
);
|
|
2257
|
+
}
|
|
2241
2258
|
driverPlugin = new DriverPlugin2(
|
|
2242
2259
|
new TursoDriver({ url: dbUrl, authToken: dbAuthToken })
|
|
2243
2260
|
);
|
|
@@ -2348,12 +2365,42 @@ function resolveDefaultArtifactPath(explicitPath, cwd = process.cwd()) {
|
|
|
2348
2365
|
}
|
|
2349
2366
|
async function createDefaultHostConfig(options = {}) {
|
|
2350
2367
|
const { requireArtifact = true, ...standaloneOpts } = options;
|
|
2351
|
-
|
|
2368
|
+
let resolvedArtifact = resolveDefaultArtifactPath(standaloneOpts.artifactPath);
|
|
2352
2369
|
if (!resolvedArtifact && requireArtifact) {
|
|
2353
2370
|
throw new Error(
|
|
2354
2371
|
"[createDefaultHostConfig] No artifact source available. Set OS_ARTIFACT_PATH (file path or http(s):// URL), place the artifact at <cwd>/dist/objectstack.json, or pass `{ artifactPath: ... }` explicitly. To boot an empty kernel anyway, pass `{ requireArtifact: false }`."
|
|
2355
2372
|
);
|
|
2356
2373
|
}
|
|
2374
|
+
if (!resolvedArtifact && !requireArtifact) {
|
|
2375
|
+
const home = resolveObjectStackHome();
|
|
2376
|
+
const stubPath = (0, import_node_path3.resolve)(home, "dist/objectstack.json");
|
|
2377
|
+
if (!(0, import_node_fs2.existsSync)(stubPath)) {
|
|
2378
|
+
(0, import_node_fs2.mkdirSync)((0, import_node_path3.resolve)(stubPath, ".."), { recursive: true });
|
|
2379
|
+
(0, import_node_fs2.writeFileSync)(
|
|
2380
|
+
stubPath,
|
|
2381
|
+
JSON.stringify(
|
|
2382
|
+
{
|
|
2383
|
+
manifest: {
|
|
2384
|
+
id: "com.objectstack.empty",
|
|
2385
|
+
name: "empty",
|
|
2386
|
+
version: "0.0.0",
|
|
2387
|
+
type: "app",
|
|
2388
|
+
description: "Empty starter kernel \u2014 install apps via the Studio marketplace."
|
|
2389
|
+
},
|
|
2390
|
+
objects: [],
|
|
2391
|
+
views: [],
|
|
2392
|
+
apps: [],
|
|
2393
|
+
flows: [],
|
|
2394
|
+
requires: []
|
|
2395
|
+
},
|
|
2396
|
+
null,
|
|
2397
|
+
2
|
|
2398
|
+
),
|
|
2399
|
+
"utf8"
|
|
2400
|
+
);
|
|
2401
|
+
}
|
|
2402
|
+
resolvedArtifact = stubPath;
|
|
2403
|
+
}
|
|
2357
2404
|
return createStandaloneStack({
|
|
2358
2405
|
...standaloneOpts,
|
|
2359
2406
|
artifactPath: resolvedArtifact
|
|
@@ -6901,6 +6948,8 @@ var KernelManager = class {
|
|
|
6901
6948
|
this.maxSize = config.maxSize ?? 32;
|
|
6902
6949
|
this.ttlMs = config.ttlMs ?? 15 * 60 * 1e3;
|
|
6903
6950
|
this.logger = config.logger ?? console;
|
|
6951
|
+
this.freshnessProbe = config.freshnessProbe;
|
|
6952
|
+
this.staleCheckIntervalMs = config.staleCheckIntervalMs ?? 1e4;
|
|
6904
6953
|
}
|
|
6905
6954
|
/** Returns the currently cached environmentIds (ordered by insertion). */
|
|
6906
6955
|
keys() {
|
|
@@ -6923,8 +6972,31 @@ var KernelManager = class {
|
|
|
6923
6972
|
if (this.ttlMs > 0 && Date.now() - existing.lastAccess > this.ttlMs) {
|
|
6924
6973
|
await this.evict(environmentId);
|
|
6925
6974
|
} else {
|
|
6926
|
-
|
|
6927
|
-
|
|
6975
|
+
if (this.freshnessProbe) {
|
|
6976
|
+
const now = Date.now();
|
|
6977
|
+
if (now - existing.lastStaleCheckAt >= this.staleCheckIntervalMs) {
|
|
6978
|
+
existing.lastStaleCheckAt = now;
|
|
6979
|
+
let stale = false;
|
|
6980
|
+
try {
|
|
6981
|
+
stale = await this.freshnessProbe(environmentId, existing.createdAt);
|
|
6982
|
+
} catch (err) {
|
|
6983
|
+
this.logger.warn?.("[KernelManager] freshness probe failed", { environmentId, err });
|
|
6984
|
+
}
|
|
6985
|
+
if (stale) {
|
|
6986
|
+
this.logger.info?.("[KernelManager] kernel evicted by freshness probe", { environmentId });
|
|
6987
|
+
await this.evict(environmentId);
|
|
6988
|
+
} else {
|
|
6989
|
+
existing.lastAccess = Date.now();
|
|
6990
|
+
return existing.kernel;
|
|
6991
|
+
}
|
|
6992
|
+
} else {
|
|
6993
|
+
existing.lastAccess = Date.now();
|
|
6994
|
+
return existing.kernel;
|
|
6995
|
+
}
|
|
6996
|
+
} else {
|
|
6997
|
+
existing.lastAccess = Date.now();
|
|
6998
|
+
return existing.kernel;
|
|
6999
|
+
}
|
|
6928
7000
|
}
|
|
6929
7001
|
}
|
|
6930
7002
|
const inflight = this.pending.get(environmentId);
|
|
@@ -6932,7 +7004,7 @@ var KernelManager = class {
|
|
|
6932
7004
|
const promise = (async () => {
|
|
6933
7005
|
const kernel = await this.factory.create(environmentId);
|
|
6934
7006
|
const now = Date.now();
|
|
6935
|
-
this.cache.set(environmentId, { kernel, createdAt: now, lastAccess: now });
|
|
7007
|
+
this.cache.set(environmentId, { kernel, createdAt: now, lastAccess: now, lastStaleCheckAt: now });
|
|
6936
7008
|
await this.enforceMaxSize();
|
|
6937
7009
|
return kernel;
|
|
6938
7010
|
})();
|
|
@@ -7100,6 +7172,30 @@ var ArtifactApiClient = class {
|
|
|
7100
7172
|
if (!found?.headCommitId) return null;
|
|
7101
7173
|
return { commitId: String(found.headCommitId), publishedAt: found.headPublishedAt ?? null };
|
|
7102
7174
|
}
|
|
7175
|
+
/**
|
|
7176
|
+
* Cheap freshness probe — returns the env's `last_published_at`
|
|
7177
|
+
* (and best-effort current commit) without rebuilding the artifact.
|
|
7178
|
+
* Used by `KernelManager` on cache hits to detect when a per-env
|
|
7179
|
+
* kernel has been invalidated by an upstream change (marketplace
|
|
7180
|
+
* install/uninstall, artifact publish) so it can be rebuilt
|
|
7181
|
+
* without waiting for the 15-minute LRU TTL to expire.
|
|
7182
|
+
*
|
|
7183
|
+
* Returns `null` on definitive 404 / unknown env. Errors propagate
|
|
7184
|
+
* (caller decides whether to treat unreachable cloud as fresh or
|
|
7185
|
+
* stale — typically fresh, so a brief outage doesn't churn every
|
|
7186
|
+
* cached kernel).
|
|
7187
|
+
*/
|
|
7188
|
+
async getFreshness(environmentId) {
|
|
7189
|
+
const url = `${this.base}/api/v1/cloud/environments/${encodeURIComponent(environmentId)}/freshness`;
|
|
7190
|
+
const res = await this.request(url);
|
|
7191
|
+
if (res === null) return null;
|
|
7192
|
+
const body = res.success === false ? null : res.data ?? res;
|
|
7193
|
+
if (!body || typeof body !== "object") return null;
|
|
7194
|
+
const envId = typeof body.environmentId === "string" ? body.environmentId : environmentId;
|
|
7195
|
+
const lastPublishedAt = typeof body.lastPublishedAt === "string" ? body.lastPublishedAt : null;
|
|
7196
|
+
const commitId = typeof body.commitId === "string" ? body.commitId : null;
|
|
7197
|
+
return { environmentId: envId, lastPublishedAt, commitId };
|
|
7198
|
+
}
|
|
7103
7199
|
/** Drop cached entries for a project (and any matching hostname). */
|
|
7104
7200
|
invalidate(environmentId) {
|
|
7105
7201
|
this.artifactCache.delete(environmentId);
|
|
@@ -7313,7 +7409,14 @@ async function createDriver(driverType, databaseUrl, authToken) {
|
|
|
7313
7409
|
}
|
|
7314
7410
|
case "libsql":
|
|
7315
7411
|
case "turso": {
|
|
7316
|
-
|
|
7412
|
+
let TursoDriver;
|
|
7413
|
+
try {
|
|
7414
|
+
({ TursoDriver } = await import("@objectstack/driver-turso"));
|
|
7415
|
+
} catch (err) {
|
|
7416
|
+
throw new Error(
|
|
7417
|
+
`[ArtifactEnvironmentRegistry] libsql/turso driver requested but @objectstack/driver-turso is not installed. Install it with: npm install @objectstack/driver-turso. (${err?.message ?? err})`
|
|
7418
|
+
);
|
|
7419
|
+
}
|
|
7317
7420
|
return new TursoDriver({ url: databaseUrl, authToken });
|
|
7318
7421
|
}
|
|
7319
7422
|
case "postgres":
|
|
@@ -7851,7 +7954,30 @@ var ArtifactKernelFactory = class {
|
|
|
7851
7954
|
};
|
|
7852
7955
|
|
|
7853
7956
|
// src/cloud/auth-proxy-plugin.ts
|
|
7957
|
+
var import_node_crypto3 = require("crypto");
|
|
7854
7958
|
var AUTH_PREFIX = "/api/v1/auth";
|
|
7959
|
+
function signSessionCookieValue(rawToken, secret) {
|
|
7960
|
+
const signature = (0, import_node_crypto3.createHmac)("sha256", secret).update(rawToken).digest("base64");
|
|
7961
|
+
return encodeURIComponent(`${rawToken}.${signature}`);
|
|
7962
|
+
}
|
|
7963
|
+
function buildSetCookieHeader(name, encodedValue, attrs, maxAgeSec) {
|
|
7964
|
+
const parts = [`${name}=${encodedValue}`];
|
|
7965
|
+
const a = attrs ?? {};
|
|
7966
|
+
if (a.path) parts.push(`Path=${a.path}`);
|
|
7967
|
+
else parts.push("Path=/");
|
|
7968
|
+
if (Number.isFinite(maxAgeSec) && maxAgeSec > 0) parts.push(`Max-Age=${Math.floor(maxAgeSec)}`);
|
|
7969
|
+
if (a.domain) parts.push(`Domain=${a.domain}`);
|
|
7970
|
+
if (a.sameSite) {
|
|
7971
|
+
const ss = String(a.sameSite);
|
|
7972
|
+
parts.push(`SameSite=${ss.charAt(0).toUpperCase() + ss.slice(1)}`);
|
|
7973
|
+
} else {
|
|
7974
|
+
parts.push("SameSite=Lax");
|
|
7975
|
+
}
|
|
7976
|
+
if (a.secure) parts.push("Secure");
|
|
7977
|
+
if (a.httpOnly !== false) parts.push("HttpOnly");
|
|
7978
|
+
if (a.partitioned) parts.push("Partitioned");
|
|
7979
|
+
return parts.join("; ");
|
|
7980
|
+
}
|
|
7855
7981
|
function pickHandler(svc) {
|
|
7856
7982
|
if (!svc) return void 0;
|
|
7857
7983
|
if (typeof svc.handleRequest === "function") return svc.handleRequest.bind(svc);
|
|
@@ -7945,6 +8071,115 @@ var AuthProxyPlugin = class {
|
|
|
7945
8071
|
return c.json({ hasOwner: true });
|
|
7946
8072
|
}
|
|
7947
8073
|
}
|
|
8074
|
+
if (c.req.method === "POST" && subPath === "sso-handoff-issue") {
|
|
8075
|
+
try {
|
|
8076
|
+
const expected = (process.env.OS_CLOUD_API_KEY ?? "").trim();
|
|
8077
|
+
if (!expected) {
|
|
8078
|
+
return c.json({ error: "sso_handoff_disabled", reason: "OS_CLOUD_API_KEY unset on env runtime" }, 503);
|
|
8079
|
+
}
|
|
8080
|
+
const authz = c.req.header("authorization") ?? "";
|
|
8081
|
+
const provided = authz.toLowerCase().startsWith("bearer ") ? authz.slice(7).trim() : "";
|
|
8082
|
+
if (!provided || provided !== expected) {
|
|
8083
|
+
return c.json({ error: "unauthorized" }, 401);
|
|
8084
|
+
}
|
|
8085
|
+
if (typeof authSvc?.getAuthContext !== "function") {
|
|
8086
|
+
return c.json({ error: "auth_service_unavailable" }, 503);
|
|
8087
|
+
}
|
|
8088
|
+
const handoffAuthCtx = await authSvc.getAuthContext();
|
|
8089
|
+
const internal = handoffAuthCtx?.internalAdapter;
|
|
8090
|
+
if (!internal?.createVerificationValue) {
|
|
8091
|
+
return c.json({ error: "verification_api_unavailable" }, 503);
|
|
8092
|
+
}
|
|
8093
|
+
let body = {};
|
|
8094
|
+
try {
|
|
8095
|
+
body = await c.req.json();
|
|
8096
|
+
} catch {
|
|
8097
|
+
body = {};
|
|
8098
|
+
}
|
|
8099
|
+
const email = String(body?.email ?? "").toLowerCase().trim();
|
|
8100
|
+
if (!email) return c.json({ error: "email_required" }, 400);
|
|
8101
|
+
const name = body?.name == null ? null : String(body.name);
|
|
8102
|
+
const by = body?.by == null ? "service" : String(body.by);
|
|
8103
|
+
const envIdInBody = body?.envId == null ? null : String(body.envId);
|
|
8104
|
+
const handoff = (0, import_node_crypto3.randomUUID)().replace(/-/g, "") + (0, import_node_crypto3.randomUUID)().replace(/-/g, "");
|
|
8105
|
+
const ttlSec = 60;
|
|
8106
|
+
const expiresAt = new Date(Date.now() + ttlSec * 1e3);
|
|
8107
|
+
await internal.createVerificationValue({
|
|
8108
|
+
identifier: `sso-handoff:${handoff}`,
|
|
8109
|
+
value: JSON.stringify({ email, name, by, envId: envIdInBody ?? environmentId }),
|
|
8110
|
+
expiresAt
|
|
8111
|
+
});
|
|
8112
|
+
return c.json({
|
|
8113
|
+
token: handoff,
|
|
8114
|
+
expiresAt: expiresAt.toISOString(),
|
|
8115
|
+
ttlSec
|
|
8116
|
+
});
|
|
8117
|
+
} catch (err) {
|
|
8118
|
+
ctx.logger?.error?.("[AuthProxyPlugin] sso-handoff-issue failed", err instanceof Error ? err : new Error(String(err)));
|
|
8119
|
+
return c.json({ error: "sso_handoff_issue_failed", message: String(err?.message ?? err) }, 500);
|
|
8120
|
+
}
|
|
8121
|
+
}
|
|
8122
|
+
if (c.req.method === "GET" && subPath === "sso-exchange") {
|
|
8123
|
+
try {
|
|
8124
|
+
const token = (url.searchParams.get("token") ?? "").trim();
|
|
8125
|
+
const nextRaw = url.searchParams.get("next") ?? "/";
|
|
8126
|
+
const next = nextRaw.startsWith("/") ? nextRaw : "/";
|
|
8127
|
+
if (!token) return c.text("missing token", 400);
|
|
8128
|
+
if (typeof authSvc?.getAuthContext !== "function") {
|
|
8129
|
+
return c.text("auth service unavailable", 503);
|
|
8130
|
+
}
|
|
8131
|
+
const authCtx = await authSvc.getAuthContext();
|
|
8132
|
+
const internal = authCtx?.internalAdapter;
|
|
8133
|
+
if (!internal?.consumeVerificationValue) {
|
|
8134
|
+
return c.text("verification API unavailable", 503);
|
|
8135
|
+
}
|
|
8136
|
+
const consumed = await internal.consumeVerificationValue(`sso-handoff:${token}`);
|
|
8137
|
+
if (!consumed) return c.text("invalid or expired token", 401);
|
|
8138
|
+
const expiresAt = consumed?.expiresAt ? new Date(consumed.expiresAt).getTime() : 0;
|
|
8139
|
+
if (!expiresAt || expiresAt < Date.now()) return c.text("expired token", 401);
|
|
8140
|
+
let payload = {};
|
|
8141
|
+
try {
|
|
8142
|
+
payload = JSON.parse(String(consumed.value));
|
|
8143
|
+
} catch {
|
|
8144
|
+
payload = { email: String(consumed.value) };
|
|
8145
|
+
}
|
|
8146
|
+
const email = String(payload.email ?? "").toLowerCase().trim();
|
|
8147
|
+
if (!email) return c.text("handoff missing email", 400);
|
|
8148
|
+
const found = await internal.findUserByEmail(email, { includeAccounts: true });
|
|
8149
|
+
let userId = found?.user?.id;
|
|
8150
|
+
let hasCredentialAccount = (found?.accounts ?? []).some((a) => a.providerId === "credential" && a.password);
|
|
8151
|
+
if (!userId) {
|
|
8152
|
+
const created = await internal.createUser({
|
|
8153
|
+
email,
|
|
8154
|
+
name: payload.name ?? email,
|
|
8155
|
+
emailVerified: true
|
|
8156
|
+
});
|
|
8157
|
+
userId = created?.id;
|
|
8158
|
+
hasCredentialAccount = false;
|
|
8159
|
+
}
|
|
8160
|
+
if (!userId) return c.text("failed to provision user", 500);
|
|
8161
|
+
const session = await internal.createSession(userId, false);
|
|
8162
|
+
const rawToken = session?.token;
|
|
8163
|
+
const sessionExpiresAt = session?.expiresAt ? new Date(session.expiresAt) : new Date(Date.now() + 7 * 24 * 3600 * 1e3);
|
|
8164
|
+
if (!rawToken) return c.text("failed to mint session", 500);
|
|
8165
|
+
const secret = authCtx?.secret ?? "";
|
|
8166
|
+
if (!secret) return c.text("auth secret unavailable", 503);
|
|
8167
|
+
const cookieName = authCtx?.authCookies?.sessionToken?.name ?? "better-auth.session_token";
|
|
8168
|
+
const cookieAttrs = authCtx?.authCookies?.sessionToken?.attributes ?? {};
|
|
8169
|
+
const encoded = signSessionCookieValue(rawToken, secret);
|
|
8170
|
+
const maxAgeSec = Math.max(60, Math.floor((sessionExpiresAt.getTime() - Date.now()) / 1e3));
|
|
8171
|
+
const setCookie = buildSetCookieHeader(cookieName, encoded, cookieAttrs, maxAgeSec);
|
|
8172
|
+
const finalNext = hasCredentialAccount ? next : `/_console/system/profile?recovery_needed=true&next=${encodeURIComponent(next)}`;
|
|
8173
|
+
const headers = new Headers();
|
|
8174
|
+
headers.set("Set-Cookie", setCookie);
|
|
8175
|
+
headers.set("Location", finalNext);
|
|
8176
|
+
headers.set("Cache-Control", "no-store");
|
|
8177
|
+
return new Response(null, { status: 302, headers });
|
|
8178
|
+
} catch (err) {
|
|
8179
|
+
ctx.logger?.error?.("[AuthProxyPlugin] sso-exchange failed", err instanceof Error ? err : new Error(String(err)));
|
|
8180
|
+
return c.text(`sso-exchange failed: ${err?.message ?? String(err)}`, 500);
|
|
8181
|
+
}
|
|
8182
|
+
}
|
|
7948
8183
|
const fn = await resolveAuthHandler(authSvc);
|
|
7949
8184
|
if (!fn) {
|
|
7950
8185
|
return c.json({ error: "auth_service_unavailable", environmentId }, 503);
|
|
@@ -8127,20 +8362,46 @@ var RuntimeConfigPlugin = class {
|
|
|
8127
8362
|
return;
|
|
8128
8363
|
}
|
|
8129
8364
|
const rawApp = httpServer.getRawApp();
|
|
8130
|
-
const
|
|
8131
|
-
|
|
8132
|
-
|
|
8133
|
-
|
|
8134
|
-
|
|
8135
|
-
|
|
8365
|
+
const features = {
|
|
8366
|
+
installLocal: this.installLocal,
|
|
8367
|
+
marketplace: true
|
|
8368
|
+
};
|
|
8369
|
+
let envRegistry = null;
|
|
8370
|
+
try {
|
|
8371
|
+
envRegistry = ctx.getService("env-registry");
|
|
8372
|
+
} catch {
|
|
8373
|
+
}
|
|
8374
|
+
const handler = async (c) => {
|
|
8375
|
+
const rawHost = c.req.header("host") ?? "";
|
|
8376
|
+
const host = rawHost.split(":")[0].toLowerCase().trim();
|
|
8377
|
+
let defaultEnvironmentId;
|
|
8378
|
+
let defaultOrgId;
|
|
8379
|
+
let resolvedSingleEnv = this.singleEnvironment;
|
|
8380
|
+
if (envRegistry && host && typeof envRegistry.resolveHostname === "function") {
|
|
8381
|
+
try {
|
|
8382
|
+
const resolved = await envRegistry.resolveHostname(host);
|
|
8383
|
+
if (resolved?.environmentId) {
|
|
8384
|
+
defaultEnvironmentId = resolved.environmentId;
|
|
8385
|
+
if (resolved.organizationId) defaultOrgId = String(resolved.organizationId);
|
|
8386
|
+
resolvedSingleEnv = true;
|
|
8387
|
+
}
|
|
8388
|
+
} catch {
|
|
8389
|
+
}
|
|
8136
8390
|
}
|
|
8391
|
+
return c.json({
|
|
8392
|
+
cloudUrl: this.cloudUrl,
|
|
8393
|
+
singleEnvironment: resolvedSingleEnv,
|
|
8394
|
+
defaultOrgId,
|
|
8395
|
+
defaultEnvironmentId,
|
|
8396
|
+
features
|
|
8397
|
+
});
|
|
8137
8398
|
};
|
|
8138
|
-
const handler = (c) => c.json(payload);
|
|
8139
8399
|
rawApp.get("/api/v1/runtime/config", handler);
|
|
8140
8400
|
rawApp.get("/api/v1/studio/runtime-config", handler);
|
|
8141
8401
|
ctx.logger?.info?.("[RuntimeConfigPlugin] mounted /api/v1/runtime/config", {
|
|
8142
8402
|
cloudUrl: this.cloudUrl || "(empty)",
|
|
8143
|
-
installLocal: this.installLocal
|
|
8403
|
+
installLocal: this.installLocal,
|
|
8404
|
+
perHostEnvResolution: !!envRegistry
|
|
8144
8405
|
});
|
|
8145
8406
|
});
|
|
8146
8407
|
};
|
|
@@ -8341,7 +8602,21 @@ var ObjectOSEnvironmentPlugin = class {
|
|
|
8341
8602
|
factory,
|
|
8342
8603
|
maxSize: this.config.kernelCacheSize,
|
|
8343
8604
|
ttlMs: this.config.kernelTtlMs,
|
|
8344
|
-
logger: ctx.logger
|
|
8605
|
+
logger: ctx.logger,
|
|
8606
|
+
// Only the HTTP client exposes /freshness; file-mode (CLI dev)
|
|
8607
|
+
// has no upstream to probe.
|
|
8608
|
+
freshnessProbe: this.config.controlPlaneUrl === "file" ? void 0 : async (envId, builtAtMs) => {
|
|
8609
|
+
const fresh = await client.getFreshness(envId);
|
|
8610
|
+
if (!fresh) return false;
|
|
8611
|
+
const t = fresh.lastPublishedAt ? Date.parse(fresh.lastPublishedAt) : NaN;
|
|
8612
|
+
if (!Number.isFinite(t)) return false;
|
|
8613
|
+
if (t <= builtAtMs) return false;
|
|
8614
|
+
try {
|
|
8615
|
+
client.invalidate(envId);
|
|
8616
|
+
} catch {
|
|
8617
|
+
}
|
|
8618
|
+
return true;
|
|
8619
|
+
}
|
|
8345
8620
|
});
|
|
8346
8621
|
this.kernelManager = kernelManager;
|
|
8347
8622
|
ctx.registerService("env-registry", envRegistry);
|
|
@@ -8942,6 +9217,7 @@ __reExport(index_exports, require("@objectstack/core"), module.exports);
|
|
|
8942
9217
|
resolveDefaultArtifactPath,
|
|
8943
9218
|
resolveErrorReporter,
|
|
8944
9219
|
resolveMetrics,
|
|
9220
|
+
resolveObjectStackHome,
|
|
8945
9221
|
resolveRequestId,
|
|
8946
9222
|
seedPlatformSsoClient,
|
|
8947
9223
|
...require("@objectstack/core")
|