@objectstack/runtime 7.6.0 → 7.8.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.d.cts CHANGED
@@ -1959,6 +1959,26 @@ declare class FileArtifactApiClient {
1959
1959
  private defaultLocalSqliteRuntime;
1960
1960
  }
1961
1961
 
1962
+ /**
1963
+ * createObjectOSStack
1964
+ *
1965
+ * ObjectOS pure-runtime stack — no control-plane database, no auth /
1966
+ * security / audit / tenant plugins. The host kernel registers:
1967
+ *
1968
+ * - A minimal engine triplet (ObjectQL + in-memory DriverPlugin +
1969
+ * MetadataPlugin) so CLI auto-injected plugins (Setup, Studio,
1970
+ * Dispatcher, REST) and the runtime can boot. The host kernel itself
1971
+ * never reads or writes business data — every record query is routed
1972
+ * to a per-project kernel built from a remote artifact.
1973
+ * - The `env-registry` and `kernel-manager` services, so the runtime's
1974
+ * HTTP dispatcher can resolve hostnames and dispatch every request
1975
+ * to the matching project kernel.
1976
+ *
1977
+ * Invoked by `createRuntimeStack()` whenever `OS_CLOUD_URL`
1978
+ * (or `config.controlPlaneUrl`) is set. The same plugin shape is returned
1979
+ * as `createCloudStack()` so host configs can swap stacks transparently.
1980
+ */
1981
+
1962
1982
  interface ObjectOSStackConfig {
1963
1983
  /**
1964
1984
  * Control-plane base URL (HTTP) or a sentinel of `'file'` for the
@@ -1992,6 +2012,21 @@ interface ObjectOSStackConfig {
1992
2012
  artifactCacheTtlMs?: number;
1993
2013
  /** API prefix (carried for parity with cloud-stack). Default: /api/v1. */
1994
2014
  apiPrefix?: string;
2015
+ /**
2016
+ * Host-supplied runtime plugins appended to the stack's default plugin
2017
+ * list. This is the official seam for a host (e.g. the ObjectStack Cloud
2018
+ * repo) to add **product/policy** plugins — marketplace install, cloud-
2019
+ * account binding, set-initial-password — to the otherwise-neutral
2020
+ * framework runtime, WITHOUT a framework release and without reaching into
2021
+ * the returned array by hand.
2022
+ *
2023
+ * They are appended last, so they mount their routes after the framework
2024
+ * plugins and can override/augment behaviour (e.g. supply a credentialled
2025
+ * install path that the browse-only MarketplaceProxyPlugin deliberately
2026
+ * does not). See docs/design/cloud-account-binding-marketplace-install.md
2027
+ * (ADR §5.2 — "framework exposes seams; cloud supplies metadata + policy").
2028
+ */
2029
+ extraPlugins?: Plugin[];
1995
2030
  }
1996
2031
  interface ObjectOSStackResult {
1997
2032
  plugins: any[];
package/dist/index.d.ts CHANGED
@@ -1959,6 +1959,26 @@ declare class FileArtifactApiClient {
1959
1959
  private defaultLocalSqliteRuntime;
1960
1960
  }
1961
1961
 
1962
+ /**
1963
+ * createObjectOSStack
1964
+ *
1965
+ * ObjectOS pure-runtime stack — no control-plane database, no auth /
1966
+ * security / audit / tenant plugins. The host kernel registers:
1967
+ *
1968
+ * - A minimal engine triplet (ObjectQL + in-memory DriverPlugin +
1969
+ * MetadataPlugin) so CLI auto-injected plugins (Setup, Studio,
1970
+ * Dispatcher, REST) and the runtime can boot. The host kernel itself
1971
+ * never reads or writes business data — every record query is routed
1972
+ * to a per-project kernel built from a remote artifact.
1973
+ * - The `env-registry` and `kernel-manager` services, so the runtime's
1974
+ * HTTP dispatcher can resolve hostnames and dispatch every request
1975
+ * to the matching project kernel.
1976
+ *
1977
+ * Invoked by `createRuntimeStack()` whenever `OS_CLOUD_URL`
1978
+ * (or `config.controlPlaneUrl`) is set. The same plugin shape is returned
1979
+ * as `createCloudStack()` so host configs can swap stacks transparently.
1980
+ */
1981
+
1962
1982
  interface ObjectOSStackConfig {
1963
1983
  /**
1964
1984
  * Control-plane base URL (HTTP) or a sentinel of `'file'` for the
@@ -1992,6 +2012,21 @@ interface ObjectOSStackConfig {
1992
2012
  artifactCacheTtlMs?: number;
1993
2013
  /** API prefix (carried for parity with cloud-stack). Default: /api/v1. */
1994
2014
  apiPrefix?: string;
2015
+ /**
2016
+ * Host-supplied runtime plugins appended to the stack's default plugin
2017
+ * list. This is the official seam for a host (e.g. the ObjectStack Cloud
2018
+ * repo) to add **product/policy** plugins — marketplace install, cloud-
2019
+ * account binding, set-initial-password — to the otherwise-neutral
2020
+ * framework runtime, WITHOUT a framework release and without reaching into
2021
+ * the returned array by hand.
2022
+ *
2023
+ * They are appended last, so they mount their routes after the framework
2024
+ * plugins and can override/augment behaviour (e.g. supply a credentialled
2025
+ * install path that the browse-only MarketplaceProxyPlugin deliberately
2026
+ * does not). See docs/design/cloud-account-binding-marketplace-install.md
2027
+ * (ADR §5.2 — "framework exposes seams; cloud supplies metadata + policy").
2028
+ */
2029
+ extraPlugins?: Plugin[];
1995
2030
  }
1996
2031
  interface ObjectOSStackResult {
1997
2032
  plugins: any[];
package/dist/index.js CHANGED
@@ -300,6 +300,24 @@ var init_seed_loader = __esm({
300
300
  for (const ref of objectRefs) {
301
301
  const fieldValue = record[ref.field];
302
302
  if (fieldValue === void 0 || fieldValue === null) continue;
303
+ if (typeof fieldValue === "object") {
304
+ const wrapped = fieldValue.externalId;
305
+ const hint = wrapped !== void 0 ? ` Pass the natural key directly: ${ref.field}: ${JSON.stringify(wrapped)}.` : ` Pass the target's ${ref.targetField} value as a plain string.`;
306
+ const error = {
307
+ sourceObject: objectName,
308
+ field: ref.field,
309
+ targetObject: ref.targetObject,
310
+ targetField: ref.targetField,
311
+ attemptedValue: fieldValue,
312
+ recordIndex: i,
313
+ message: `Invalid reference for ${objectName}.${ref.field}: expected a ${ref.targetObject}.${ref.targetField} natural-key string but got an object.${hint}`
314
+ };
315
+ errors.push(error);
316
+ allErrors.push(error);
317
+ this.logger.warn(`[SeedLoader] ${error.message}`, { recordIndex: i });
318
+ record[ref.field] = null;
319
+ continue;
320
+ }
303
321
  if (typeof fieldValue !== "string" || this.looksLikeInternalId(fieldValue)) continue;
304
322
  const targetMap = insertedRecords.get(ref.targetObject);
305
323
  const resolvedId = targetMap?.get(String(fieldValue));
@@ -3514,6 +3532,23 @@ var _HttpDispatcher = class _HttpDispatcher {
3514
3532
  return { handled: true, response: this.error(e.message, 404) };
3515
3533
  }
3516
3534
  }
3535
+ if (parts.length === 1 && parts[0] === "_drafts" && (!method || method.toUpperCase() === "GET")) {
3536
+ const protocol = await this.resolveService("protocol");
3537
+ if (protocol && typeof protocol.listDrafts === "function") {
3538
+ try {
3539
+ const organizationId = await this.resolveActiveOrganizationId(_context);
3540
+ const data = await protocol.listDrafts({
3541
+ packageId: query?.packageId || void 0,
3542
+ type: query?.type || void 0,
3543
+ organizationId
3544
+ });
3545
+ return { handled: true, response: this.success(data) };
3546
+ } catch (e) {
3547
+ return { handled: true, response: this.error(e.message, 500) };
3548
+ }
3549
+ }
3550
+ return { handled: true, response: this.error("Draft listing not supported", 501) };
3551
+ }
3517
3552
  if (parts.length === 1) {
3518
3553
  const typeOrName = parts[0];
3519
3554
  const packageId = query?.package || void 0;
@@ -3813,7 +3848,15 @@ var _HttpDispatcher = class _HttpDispatcher {
3813
3848
  return { handled: true, response: this.success({ packages, total: packages.length }) };
3814
3849
  }
3815
3850
  if (parts.length === 0 && m === "POST") {
3816
- const pkg = registry.installPackage(body.manifest || body, body.settings);
3851
+ const manifest = body.manifest || body;
3852
+ let pkg;
3853
+ const protocolSvc = await this.resolveService("protocol").catch(() => null);
3854
+ if (protocolSvc && typeof protocolSvc.installPackage === "function") {
3855
+ const out = await protocolSvc.installPackage({ manifest, settings: body.settings });
3856
+ pkg = out?.package ?? out;
3857
+ } else {
3858
+ pkg = registry.installPackage(manifest, body.settings);
3859
+ }
3817
3860
  const res = this.success(pkg);
3818
3861
  res.status = 201;
3819
3862
  return { handled: true, response: res };
@@ -3849,6 +3892,24 @@ var _HttpDispatcher = class _HttpDispatcher {
3849
3892
  }
3850
3893
  return { handled: true, response: this.error("Metadata service not available", 503) };
3851
3894
  }
3895
+ if (parts.length === 2 && parts[1] === "publish-drafts" && m === "POST") {
3896
+ const id = decodeURIComponent(parts[0]);
3897
+ const protocol = await this.resolveService("protocol");
3898
+ if (protocol && typeof protocol.publishPackageDrafts === "function") {
3899
+ try {
3900
+ const organizationId = await this.resolveActiveOrganizationId(_context);
3901
+ const result = await protocol.publishPackageDrafts({
3902
+ packageId: id,
3903
+ ...organizationId ? { organizationId } : {},
3904
+ ...body?.actor ? { actor: body.actor } : {}
3905
+ });
3906
+ return { handled: true, response: this.success(result) };
3907
+ } catch (e) {
3908
+ return { handled: true, response: this.error(e.message, e.statusCode || 500) };
3909
+ }
3910
+ }
3911
+ return { handled: true, response: this.error("Draft publishing not supported", 501) };
3912
+ }
3852
3913
  if (parts.length === 2 && parts[1] === "revert" && m === "POST") {
3853
3914
  const id = decodeURIComponent(parts[0]);
3854
3915
  const metadataService = await this.getService(CoreServiceName.enum.metadata);
@@ -5358,6 +5419,14 @@ function createDispatcherPlugin(config = {}) {
5358
5419
  errorResponse(err, res);
5359
5420
  }
5360
5421
  });
5422
+ server.post(`${prefix}/packages/:id/publish-drafts`, async (req, res) => {
5423
+ try {
5424
+ const result = await dispatcher.handlePackages(`/${req.params.id}/publish-drafts`, "POST", req.body, {}, { request: req });
5425
+ sendResult(result, res);
5426
+ } catch (err) {
5427
+ errorResponse(err, res);
5428
+ }
5429
+ });
5361
5430
  server.post(`${prefix}/packages/:id/revert`, async (req, res) => {
5362
5431
  try {
5363
5432
  const result = await dispatcher.handlePackages(`/${req.params.id}/revert`, "POST", req.body, {}, { request: req });
@@ -7370,6 +7439,61 @@ var AuthProxyPlugin = class {
7370
7439
  return c.text(`sso-exchange failed: ${err?.message ?? String(err)}`, 500);
7371
7440
  }
7372
7441
  }
7442
+ if (c.req.method === "POST" && subPath === "set-initial-password") {
7443
+ try {
7444
+ let body = {};
7445
+ try {
7446
+ body = await c.req.json();
7447
+ } catch {
7448
+ body = {};
7449
+ }
7450
+ const newPassword = body?.newPassword;
7451
+ if (typeof newPassword !== "string" || newPassword.length === 0) {
7452
+ return c.json({ success: false, error: { code: "invalid_request", message: "newPassword is required" } }, 400);
7453
+ }
7454
+ if (typeof authSvc?.getAuthContext !== "function") {
7455
+ return c.json({ success: false, error: { code: "unavailable", message: "Auth context unavailable" } }, 503);
7456
+ }
7457
+ let userId;
7458
+ try {
7459
+ const api = typeof authSvc.getApi === "function" ? await authSvc.getApi() : null;
7460
+ const session = await api?.getSession?.({ headers: c.req.raw.headers });
7461
+ userId = session?.user?.id ? String(session.user.id) : void 0;
7462
+ } catch {
7463
+ }
7464
+ if (!userId) {
7465
+ return c.json({ success: false, error: { code: "unauthorized", message: "Sign in first" } }, 401);
7466
+ }
7467
+ const setPwCtx = await authSvc.getAuthContext();
7468
+ if (!setPwCtx?.internalAdapter || !setPwCtx?.password) {
7469
+ return c.json({ success: false, error: { code: "unavailable", message: "Auth context unavailable" } }, 503);
7470
+ }
7471
+ const minLen = setPwCtx.password?.config?.minPasswordLength ?? 8;
7472
+ const maxLen = setPwCtx.password?.config?.maxPasswordLength ?? 128;
7473
+ if (newPassword.length < minLen) {
7474
+ return c.json({ success: false, error: { code: "password_too_short", message: `Password must be at least ${minLen} characters` } }, 400);
7475
+ }
7476
+ if (newPassword.length > maxLen) {
7477
+ return c.json({ success: false, error: { code: "password_too_long", message: `Password must be at most ${maxLen} characters` } }, 400);
7478
+ }
7479
+ const accounts = await setPwCtx.internalAdapter.findAccounts(userId);
7480
+ const existingCredential = accounts?.find?.((a) => a.providerId === "credential" && a.password);
7481
+ if (existingCredential) {
7482
+ return c.json({ success: false, error: { code: "credential_account_exists", message: "A local password is already set for this account. Use change-password instead." } }, 409);
7483
+ }
7484
+ const passwordHash = await setPwCtx.password.hash(newPassword);
7485
+ await setPwCtx.internalAdapter.createAccount({
7486
+ userId,
7487
+ providerId: "credential",
7488
+ accountId: userId,
7489
+ password: passwordHash
7490
+ });
7491
+ return c.json({ success: true });
7492
+ } catch (err) {
7493
+ ctx.logger?.error?.("[AuthProxyPlugin] set-initial-password failed", err instanceof Error ? err : new Error(String(err)));
7494
+ return c.json({ success: false, error: { code: "set_password_failed", message: String(err?.message ?? err) } }, 500);
7495
+ }
7496
+ }
7373
7497
  const fn = await resolveAuthHandler(authSvc);
7374
7498
  if (!fn) {
7375
7499
  return c.json({ error: "auth_service_unavailable", environmentId }, 503);
@@ -8084,7 +8208,17 @@ async function createObjectOSStack(config) {
8084
8208
  };
8085
8209
  const enginePlugins = await createHostEnginePlugins();
8086
8210
  return {
8087
- plugins: [...enginePlugins, new ObjectOSEnvironmentPlugin(merged), new AuthProxyPlugin(), new MarketplaceProxyPlugin({ controlPlaneUrl: merged.controlPlaneUrl === "file" ? void 0 : merged.controlPlaneUrl }), new RuntimeConfigPlugin({ controlPlaneUrl: merged.controlPlaneUrl === "file" ? void 0 : merged.controlPlaneUrl, installLocal: false })],
8211
+ plugins: [
8212
+ ...enginePlugins,
8213
+ new ObjectOSEnvironmentPlugin(merged),
8214
+ new AuthProxyPlugin(),
8215
+ new MarketplaceProxyPlugin({ controlPlaneUrl: merged.controlPlaneUrl === "file" ? void 0 : merged.controlPlaneUrl }),
8216
+ new RuntimeConfigPlugin({ controlPlaneUrl: merged.controlPlaneUrl === "file" ? void 0 : merged.controlPlaneUrl, installLocal: false }),
8217
+ // Host-supplied product/policy plugins (the official seam — see
8218
+ // ObjectOSStackConfig.extraPlugins). Appended last so they mount
8219
+ // after the framework defaults.
8220
+ ...config.extraPlugins ?? []
8221
+ ],
8088
8222
  api: {
8089
8223
  enableProjectScoping: true,
8090
8224
  projectResolution: "auto",