@objectstack/runtime 7.7.0 → 7.9.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 +200 -17
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +60 -0
- package/dist/index.d.ts +60 -0
- package/dist/index.js +200 -17
- package/dist/index.js.map +1 -1
- package/package.json +18 -18
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,30 @@ 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[];
|
|
2030
|
+
/**
|
|
2031
|
+
* Capability tokens force-mounted on EVERY per-environment kernel, in
|
|
2032
|
+
* addition to whatever the app artifact declares in `requires`. Merged and
|
|
2033
|
+
* de-duped with `bundle.requires` before the capability loader runs. This
|
|
2034
|
+
* is the host seam for a cloud operator to make a capability ubiquitous
|
|
2035
|
+
* across all tenants without editing each app — e.g. `['ai','aiStudio']`
|
|
2036
|
+
* so every cloud environment supports AI-driven online development.
|
|
2037
|
+
*/
|
|
2038
|
+
defaultRequires?: string[];
|
|
1995
2039
|
}
|
|
1996
2040
|
interface ObjectOSStackResult {
|
|
1997
2041
|
plugins: any[];
|
|
@@ -2213,6 +2257,14 @@ interface RuntimeConfigPluginConfig {
|
|
|
2213
2257
|
controlPlaneUrl?: string;
|
|
2214
2258
|
/** Override the `features.installLocal` flag. Default: false. */
|
|
2215
2259
|
installLocal?: boolean;
|
|
2260
|
+
/**
|
|
2261
|
+
* Override the `features.aiStudio` flag — whether the SPA should surface
|
|
2262
|
+
* AI-driven metadata authoring ("online development") affordances. Default:
|
|
2263
|
+
* true (the actual authoring capability is still gated server-side by the
|
|
2264
|
+
* presence of the `metadata_assistant` agent / @objectstack/service-ai-studio
|
|
2265
|
+
* package; set false to force-hide the authoring UI for a tier/deployment).
|
|
2266
|
+
*/
|
|
2267
|
+
aiStudio?: boolean;
|
|
2216
2268
|
/**
|
|
2217
2269
|
* Report this runtime as a single-environment deployment (CLI
|
|
2218
2270
|
* `objectstack dev` / `os serve`). Defaults to `false` for
|
|
@@ -2234,6 +2286,7 @@ declare class RuntimeConfigPlugin implements Plugin {
|
|
|
2234
2286
|
readonly version = "1.0.0";
|
|
2235
2287
|
private readonly cloudUrl;
|
|
2236
2288
|
private readonly installLocal;
|
|
2289
|
+
private readonly aiStudio;
|
|
2237
2290
|
private readonly singleEnvironment;
|
|
2238
2291
|
private readonly productName;
|
|
2239
2292
|
private readonly productShortName;
|
|
@@ -2365,6 +2418,12 @@ interface ArtifactKernelFactoryConfig {
|
|
|
2365
2418
|
* `process.env.OS_AUTH_SECRET` / `AUTH_SECRET` at construction time.
|
|
2366
2419
|
*/
|
|
2367
2420
|
authBaseSecret?: string;
|
|
2421
|
+
/**
|
|
2422
|
+
* Capability tokens force-mounted on every per-environment kernel, merged
|
|
2423
|
+
* (and de-duped by the loader) with the artifact's own `requires`. Lets a
|
|
2424
|
+
* host make a capability ubiquitous across tenants — e.g. `['ai','aiStudio']`.
|
|
2425
|
+
*/
|
|
2426
|
+
defaultRequires?: string[];
|
|
2368
2427
|
}
|
|
2369
2428
|
declare class ArtifactKernelFactory implements EnvironmentKernelFactory {
|
|
2370
2429
|
private readonly client;
|
|
@@ -2372,6 +2431,7 @@ declare class ArtifactKernelFactory implements EnvironmentKernelFactory {
|
|
|
2372
2431
|
private readonly logger;
|
|
2373
2432
|
private readonly kernelConfig?;
|
|
2374
2433
|
private readonly authBaseSecret;
|
|
2434
|
+
private readonly defaultRequires;
|
|
2375
2435
|
constructor(config: ArtifactKernelFactoryConfig);
|
|
2376
2436
|
create(environmentId: string): Promise<ObjectKernel>;
|
|
2377
2437
|
}
|
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,30 @@ 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[];
|
|
2030
|
+
/**
|
|
2031
|
+
* Capability tokens force-mounted on EVERY per-environment kernel, in
|
|
2032
|
+
* addition to whatever the app artifact declares in `requires`. Merged and
|
|
2033
|
+
* de-duped with `bundle.requires` before the capability loader runs. This
|
|
2034
|
+
* is the host seam for a cloud operator to make a capability ubiquitous
|
|
2035
|
+
* across all tenants without editing each app — e.g. `['ai','aiStudio']`
|
|
2036
|
+
* so every cloud environment supports AI-driven online development.
|
|
2037
|
+
*/
|
|
2038
|
+
defaultRequires?: string[];
|
|
1995
2039
|
}
|
|
1996
2040
|
interface ObjectOSStackResult {
|
|
1997
2041
|
plugins: any[];
|
|
@@ -2213,6 +2257,14 @@ interface RuntimeConfigPluginConfig {
|
|
|
2213
2257
|
controlPlaneUrl?: string;
|
|
2214
2258
|
/** Override the `features.installLocal` flag. Default: false. */
|
|
2215
2259
|
installLocal?: boolean;
|
|
2260
|
+
/**
|
|
2261
|
+
* Override the `features.aiStudio` flag — whether the SPA should surface
|
|
2262
|
+
* AI-driven metadata authoring ("online development") affordances. Default:
|
|
2263
|
+
* true (the actual authoring capability is still gated server-side by the
|
|
2264
|
+
* presence of the `metadata_assistant` agent / @objectstack/service-ai-studio
|
|
2265
|
+
* package; set false to force-hide the authoring UI for a tier/deployment).
|
|
2266
|
+
*/
|
|
2267
|
+
aiStudio?: boolean;
|
|
2216
2268
|
/**
|
|
2217
2269
|
* Report this runtime as a single-environment deployment (CLI
|
|
2218
2270
|
* `objectstack dev` / `os serve`). Defaults to `false` for
|
|
@@ -2234,6 +2286,7 @@ declare class RuntimeConfigPlugin implements Plugin {
|
|
|
2234
2286
|
readonly version = "1.0.0";
|
|
2235
2287
|
private readonly cloudUrl;
|
|
2236
2288
|
private readonly installLocal;
|
|
2289
|
+
private readonly aiStudio;
|
|
2237
2290
|
private readonly singleEnvironment;
|
|
2238
2291
|
private readonly productName;
|
|
2239
2292
|
private readonly productShortName;
|
|
@@ -2365,6 +2418,12 @@ interface ArtifactKernelFactoryConfig {
|
|
|
2365
2418
|
* `process.env.OS_AUTH_SECRET` / `AUTH_SECRET` at construction time.
|
|
2366
2419
|
*/
|
|
2367
2420
|
authBaseSecret?: string;
|
|
2421
|
+
/**
|
|
2422
|
+
* Capability tokens force-mounted on every per-environment kernel, merged
|
|
2423
|
+
* (and de-duped by the loader) with the artifact's own `requires`. Lets a
|
|
2424
|
+
* host make a capability ubiquitous across tenants — e.g. `['ai','aiStudio']`.
|
|
2425
|
+
*/
|
|
2426
|
+
defaultRequires?: string[];
|
|
2368
2427
|
}
|
|
2369
2428
|
declare class ArtifactKernelFactory implements EnvironmentKernelFactory {
|
|
2370
2429
|
private readonly client;
|
|
@@ -2372,6 +2431,7 @@ declare class ArtifactKernelFactory implements EnvironmentKernelFactory {
|
|
|
2372
2431
|
private readonly logger;
|
|
2373
2432
|
private readonly kernelConfig?;
|
|
2374
2433
|
private readonly authBaseSecret;
|
|
2434
|
+
private readonly defaultRequires;
|
|
2375
2435
|
constructor(config: ArtifactKernelFactoryConfig);
|
|
2376
2436
|
create(environmentId: string): Promise<ObjectKernel>;
|
|
2377
2437
|
}
|
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));
|
|
@@ -3496,7 +3514,8 @@ var _HttpDispatcher = class _HttpDispatcher {
|
|
|
3496
3514
|
if (protocol && typeof protocol.getMetaItem === "function") {
|
|
3497
3515
|
try {
|
|
3498
3516
|
const organizationId = await this.resolveActiveOrganizationId(_context);
|
|
3499
|
-
const
|
|
3517
|
+
const previewDrafts = query?.preview === "draft";
|
|
3518
|
+
const data = await protocol.getMetaItem({ type: singularType, name, packageId, organizationId, previewDrafts });
|
|
3500
3519
|
return { handled: true, response: this.success(data) };
|
|
3501
3520
|
} catch (e) {
|
|
3502
3521
|
}
|
|
@@ -3514,6 +3533,23 @@ var _HttpDispatcher = class _HttpDispatcher {
|
|
|
3514
3533
|
return { handled: true, response: this.error(e.message, 404) };
|
|
3515
3534
|
}
|
|
3516
3535
|
}
|
|
3536
|
+
if (parts.length === 1 && parts[0] === "_drafts" && (!method || method.toUpperCase() === "GET")) {
|
|
3537
|
+
const protocol = await this.resolveService("protocol");
|
|
3538
|
+
if (protocol && typeof protocol.listDrafts === "function") {
|
|
3539
|
+
try {
|
|
3540
|
+
const organizationId = await this.resolveActiveOrganizationId(_context);
|
|
3541
|
+
const data = await protocol.listDrafts({
|
|
3542
|
+
packageId: query?.packageId || void 0,
|
|
3543
|
+
type: query?.type || void 0,
|
|
3544
|
+
organizationId
|
|
3545
|
+
});
|
|
3546
|
+
return { handled: true, response: this.success(data) };
|
|
3547
|
+
} catch (e) {
|
|
3548
|
+
return { handled: true, response: this.error(e.message, 500) };
|
|
3549
|
+
}
|
|
3550
|
+
}
|
|
3551
|
+
return { handled: true, response: this.error("Draft listing not supported", 501) };
|
|
3552
|
+
}
|
|
3517
3553
|
if (parts.length === 1) {
|
|
3518
3554
|
const typeOrName = parts[0];
|
|
3519
3555
|
const packageId = query?.package || void 0;
|
|
@@ -3521,7 +3557,8 @@ var _HttpDispatcher = class _HttpDispatcher {
|
|
|
3521
3557
|
if (protocol && typeof protocol.getMetaItems === "function") {
|
|
3522
3558
|
try {
|
|
3523
3559
|
const organizationId = await this.resolveActiveOrganizationId(_context);
|
|
3524
|
-
const
|
|
3560
|
+
const previewDrafts = query?.preview === "draft";
|
|
3561
|
+
const data = await protocol.getMetaItems({ type: typeOrName, packageId, organizationId, previewDrafts });
|
|
3525
3562
|
if (data && (data.items !== void 0 || Array.isArray(data))) {
|
|
3526
3563
|
return { handled: true, response: this.success(data) };
|
|
3527
3564
|
}
|
|
@@ -3813,7 +3850,15 @@ var _HttpDispatcher = class _HttpDispatcher {
|
|
|
3813
3850
|
return { handled: true, response: this.success({ packages, total: packages.length }) };
|
|
3814
3851
|
}
|
|
3815
3852
|
if (parts.length === 0 && m === "POST") {
|
|
3816
|
-
const
|
|
3853
|
+
const manifest = body.manifest || body;
|
|
3854
|
+
let pkg;
|
|
3855
|
+
const protocolSvc = await this.resolveService("protocol").catch(() => null);
|
|
3856
|
+
if (protocolSvc && typeof protocolSvc.installPackage === "function") {
|
|
3857
|
+
const out = await protocolSvc.installPackage({ manifest, settings: body.settings });
|
|
3858
|
+
pkg = out?.package ?? out;
|
|
3859
|
+
} else {
|
|
3860
|
+
pkg = registry.installPackage(manifest, body.settings);
|
|
3861
|
+
}
|
|
3817
3862
|
const res = this.success(pkg);
|
|
3818
3863
|
res.status = 201;
|
|
3819
3864
|
return { handled: true, response: res };
|
|
@@ -3849,6 +3894,42 @@ var _HttpDispatcher = class _HttpDispatcher {
|
|
|
3849
3894
|
}
|
|
3850
3895
|
return { handled: true, response: this.error("Metadata service not available", 503) };
|
|
3851
3896
|
}
|
|
3897
|
+
if (parts.length === 2 && parts[1] === "publish-drafts" && m === "POST") {
|
|
3898
|
+
const id = decodeURIComponent(parts[0]);
|
|
3899
|
+
const protocol = await this.resolveService("protocol");
|
|
3900
|
+
if (protocol && typeof protocol.publishPackageDrafts === "function") {
|
|
3901
|
+
try {
|
|
3902
|
+
const organizationId = await this.resolveActiveOrganizationId(_context);
|
|
3903
|
+
const result = await protocol.publishPackageDrafts({
|
|
3904
|
+
packageId: id,
|
|
3905
|
+
...organizationId ? { organizationId } : {},
|
|
3906
|
+
...body?.actor ? { actor: body.actor } : {}
|
|
3907
|
+
});
|
|
3908
|
+
return { handled: true, response: this.success(result) };
|
|
3909
|
+
} catch (e) {
|
|
3910
|
+
return { handled: true, response: this.error(e.message, e.statusCode || 500) };
|
|
3911
|
+
}
|
|
3912
|
+
}
|
|
3913
|
+
return { handled: true, response: this.error("Draft publishing not supported", 501) };
|
|
3914
|
+
}
|
|
3915
|
+
if (parts.length === 2 && parts[1] === "discard-drafts" && m === "POST") {
|
|
3916
|
+
const id = decodeURIComponent(parts[0]);
|
|
3917
|
+
const protocol = await this.resolveService("protocol");
|
|
3918
|
+
if (protocol && typeof protocol.discardPackageDrafts === "function") {
|
|
3919
|
+
try {
|
|
3920
|
+
const organizationId = await this.resolveActiveOrganizationId(_context);
|
|
3921
|
+
const result = await protocol.discardPackageDrafts({
|
|
3922
|
+
packageId: id,
|
|
3923
|
+
...organizationId ? { organizationId } : {},
|
|
3924
|
+
...body?.actor ? { actor: body.actor } : {}
|
|
3925
|
+
});
|
|
3926
|
+
return { handled: true, response: this.success(result) };
|
|
3927
|
+
} catch (e) {
|
|
3928
|
+
return { handled: true, response: this.error(e.message, e.statusCode || 500) };
|
|
3929
|
+
}
|
|
3930
|
+
}
|
|
3931
|
+
return { handled: true, response: this.error("Draft discarding not supported", 501) };
|
|
3932
|
+
}
|
|
3852
3933
|
if (parts.length === 2 && parts[1] === "revert" && m === "POST") {
|
|
3853
3934
|
const id = decodeURIComponent(parts[0]);
|
|
3854
3935
|
const metadataService = await this.getService(CoreServiceName.enum.metadata);
|
|
@@ -3874,9 +3955,27 @@ var _HttpDispatcher = class _HttpDispatcher {
|
|
|
3874
3955
|
}
|
|
3875
3956
|
if (parts.length === 1 && m === "DELETE") {
|
|
3876
3957
|
const id = decodeURIComponent(parts[0]);
|
|
3877
|
-
const
|
|
3878
|
-
|
|
3879
|
-
|
|
3958
|
+
const registryRemoved = registry.uninstallPackage(id);
|
|
3959
|
+
let persisted = void 0;
|
|
3960
|
+
const protocol = await this.resolveService("protocol");
|
|
3961
|
+
if (protocol && typeof protocol.deletePackage === "function") {
|
|
3962
|
+
try {
|
|
3963
|
+
const organizationId = await this.resolveActiveOrganizationId(_context);
|
|
3964
|
+
const keepData = query?.keepData === "true" || query?.keepData === "1";
|
|
3965
|
+
persisted = await protocol.deletePackage({
|
|
3966
|
+
packageId: id,
|
|
3967
|
+
...organizationId ? { organizationId } : {},
|
|
3968
|
+
...keepData ? { keepData: true } : {}
|
|
3969
|
+
});
|
|
3970
|
+
} catch (e) {
|
|
3971
|
+
return { handled: true, response: this.error(e.message, e.statusCode || 500) };
|
|
3972
|
+
}
|
|
3973
|
+
}
|
|
3974
|
+
const deletedCount = persisted?.deletedCount ?? 0;
|
|
3975
|
+
if (!registryRemoved && deletedCount === 0) {
|
|
3976
|
+
return { handled: true, response: this.error(`Package '${id}' not found`, 404) };
|
|
3977
|
+
}
|
|
3978
|
+
return { handled: true, response: this.success({ success: true, registryRemoved, persisted }) };
|
|
3880
3979
|
}
|
|
3881
3980
|
} catch (e) {
|
|
3882
3981
|
return { handled: true, response: this.error(e.message, e.statusCode || 500) };
|
|
@@ -5358,6 +5457,14 @@ function createDispatcherPlugin(config = {}) {
|
|
|
5358
5457
|
errorResponse(err, res);
|
|
5359
5458
|
}
|
|
5360
5459
|
});
|
|
5460
|
+
server.post(`${prefix}/packages/:id/publish-drafts`, async (req, res) => {
|
|
5461
|
+
try {
|
|
5462
|
+
const result = await dispatcher.handlePackages(`/${req.params.id}/publish-drafts`, "POST", req.body, {}, { request: req });
|
|
5463
|
+
sendResult(result, res);
|
|
5464
|
+
} catch (err) {
|
|
5465
|
+
errorResponse(err, res);
|
|
5466
|
+
}
|
|
5467
|
+
});
|
|
5361
5468
|
server.post(`${prefix}/packages/:id/revert`, async (req, res) => {
|
|
5362
5469
|
try {
|
|
5363
5470
|
const result = await dispatcher.handlePackages(`/${req.params.id}/revert`, "POST", req.body, {}, { request: req });
|
|
@@ -6459,6 +6566,16 @@ var CAPABILITY_PROVIDERS = {
|
|
|
6459
6566
|
pkg: "@objectstack/service-ai",
|
|
6460
6567
|
export: "AIServicePlugin"
|
|
6461
6568
|
},
|
|
6569
|
+
// AI Studio — AI-driven metadata authoring ("online development"). This is
|
|
6570
|
+
// a commercial capability that ships in the private @objectstack/service-ai-studio
|
|
6571
|
+
// package (not part of the open-source framework). The dynamic import below
|
|
6572
|
+
// silently skips when the package isn't installed, so the open-source build
|
|
6573
|
+
// is unaffected; cloud and enterprise installs that ship the package light it
|
|
6574
|
+
// up. Pair with `ai` in `requires` (it attaches via the `ai:ready` hook).
|
|
6575
|
+
aiStudio: {
|
|
6576
|
+
pkg: "@objectstack/service-ai-studio",
|
|
6577
|
+
export: "AIStudioPlugin"
|
|
6578
|
+
},
|
|
6462
6579
|
analytics: {
|
|
6463
6580
|
pkg: "@objectstack/service-analytics",
|
|
6464
6581
|
export: "AnalyticsServicePlugin",
|
|
@@ -6785,6 +6902,7 @@ var ArtifactKernelFactory = class {
|
|
|
6785
6902
|
this.envRegistry = config.envRegistry;
|
|
6786
6903
|
this.logger = config.logger ?? console;
|
|
6787
6904
|
this.kernelConfig = config.kernelConfig;
|
|
6905
|
+
this.defaultRequires = config.defaultRequires ?? [];
|
|
6788
6906
|
this.authBaseSecret = (config.authBaseSecret ?? readEnvWithDeprecation3("OS_AUTH_SECRET", ["AUTH_SECRET", "BETTER_AUTH_SECRET"]) ?? "").trim();
|
|
6789
6907
|
}
|
|
6790
6908
|
async create(environmentId) {
|
|
@@ -6951,7 +7069,10 @@ var ArtifactKernelFactory = class {
|
|
|
6951
7069
|
});
|
|
6952
7070
|
}
|
|
6953
7071
|
const requiresRaw = (Array.isArray(bundle?.requires) ? bundle.requires : null) ?? (Array.isArray(sys?.requires) ? sys.requires : null) ?? [];
|
|
6954
|
-
const requires =
|
|
7072
|
+
const requires = [
|
|
7073
|
+
...requiresRaw,
|
|
7074
|
+
...this.defaultRequires
|
|
7075
|
+
].filter((x) => typeof x === "string" && x.length > 0);
|
|
6955
7076
|
if (requires.length > 0) {
|
|
6956
7077
|
const installed = await loadCapabilities({
|
|
6957
7078
|
kernel,
|
|
@@ -7370,6 +7491,61 @@ var AuthProxyPlugin = class {
|
|
|
7370
7491
|
return c.text(`sso-exchange failed: ${err?.message ?? String(err)}`, 500);
|
|
7371
7492
|
}
|
|
7372
7493
|
}
|
|
7494
|
+
if (c.req.method === "POST" && subPath === "set-initial-password") {
|
|
7495
|
+
try {
|
|
7496
|
+
let body = {};
|
|
7497
|
+
try {
|
|
7498
|
+
body = await c.req.json();
|
|
7499
|
+
} catch {
|
|
7500
|
+
body = {};
|
|
7501
|
+
}
|
|
7502
|
+
const newPassword = body?.newPassword;
|
|
7503
|
+
if (typeof newPassword !== "string" || newPassword.length === 0) {
|
|
7504
|
+
return c.json({ success: false, error: { code: "invalid_request", message: "newPassword is required" } }, 400);
|
|
7505
|
+
}
|
|
7506
|
+
if (typeof authSvc?.getAuthContext !== "function") {
|
|
7507
|
+
return c.json({ success: false, error: { code: "unavailable", message: "Auth context unavailable" } }, 503);
|
|
7508
|
+
}
|
|
7509
|
+
let userId;
|
|
7510
|
+
try {
|
|
7511
|
+
const api = typeof authSvc.getApi === "function" ? await authSvc.getApi() : null;
|
|
7512
|
+
const session = await api?.getSession?.({ headers: c.req.raw.headers });
|
|
7513
|
+
userId = session?.user?.id ? String(session.user.id) : void 0;
|
|
7514
|
+
} catch {
|
|
7515
|
+
}
|
|
7516
|
+
if (!userId) {
|
|
7517
|
+
return c.json({ success: false, error: { code: "unauthorized", message: "Sign in first" } }, 401);
|
|
7518
|
+
}
|
|
7519
|
+
const setPwCtx = await authSvc.getAuthContext();
|
|
7520
|
+
if (!setPwCtx?.internalAdapter || !setPwCtx?.password) {
|
|
7521
|
+
return c.json({ success: false, error: { code: "unavailable", message: "Auth context unavailable" } }, 503);
|
|
7522
|
+
}
|
|
7523
|
+
const minLen = setPwCtx.password?.config?.minPasswordLength ?? 8;
|
|
7524
|
+
const maxLen = setPwCtx.password?.config?.maxPasswordLength ?? 128;
|
|
7525
|
+
if (newPassword.length < minLen) {
|
|
7526
|
+
return c.json({ success: false, error: { code: "password_too_short", message: `Password must be at least ${minLen} characters` } }, 400);
|
|
7527
|
+
}
|
|
7528
|
+
if (newPassword.length > maxLen) {
|
|
7529
|
+
return c.json({ success: false, error: { code: "password_too_long", message: `Password must be at most ${maxLen} characters` } }, 400);
|
|
7530
|
+
}
|
|
7531
|
+
const accounts = await setPwCtx.internalAdapter.findAccounts(userId);
|
|
7532
|
+
const existingCredential = accounts?.find?.((a) => a.providerId === "credential" && a.password);
|
|
7533
|
+
if (existingCredential) {
|
|
7534
|
+
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);
|
|
7535
|
+
}
|
|
7536
|
+
const passwordHash = await setPwCtx.password.hash(newPassword);
|
|
7537
|
+
await setPwCtx.internalAdapter.createAccount({
|
|
7538
|
+
userId,
|
|
7539
|
+
providerId: "credential",
|
|
7540
|
+
accountId: userId,
|
|
7541
|
+
password: passwordHash
|
|
7542
|
+
});
|
|
7543
|
+
return c.json({ success: true });
|
|
7544
|
+
} catch (err) {
|
|
7545
|
+
ctx.logger?.error?.("[AuthProxyPlugin] set-initial-password failed", err instanceof Error ? err : new Error(String(err)));
|
|
7546
|
+
return c.json({ success: false, error: { code: "set_password_failed", message: String(err?.message ?? err) } }, 500);
|
|
7547
|
+
}
|
|
7548
|
+
}
|
|
7373
7549
|
const fn = await resolveAuthHandler(authSvc);
|
|
7374
7550
|
if (!fn) {
|
|
7375
7551
|
return c.json({ error: "auth_service_unavailable", environmentId }, 503);
|
|
@@ -7555,13 +7731,7 @@ var MarketplaceProxyPlugin = class _MarketplaceProxyPlugin {
|
|
|
7555
7731
|
}
|
|
7556
7732
|
const target = `${cloudUrl}${incomingUrl.pathname}${incomingUrl.search}`;
|
|
7557
7733
|
if (method !== "GET" && method !== "HEAD") {
|
|
7558
|
-
return
|
|
7559
|
-
success: false,
|
|
7560
|
-
error: {
|
|
7561
|
-
code: "marketplace_method_not_allowed",
|
|
7562
|
-
message: `Marketplace proxy only forwards GET/HEAD; install via cloud.`
|
|
7563
|
-
}
|
|
7564
|
-
}, 405);
|
|
7734
|
+
return next();
|
|
7565
7735
|
}
|
|
7566
7736
|
const accept = c.req.header("accept") ?? "application/json";
|
|
7567
7737
|
const acceptLang = c.req.header("accept-language") ?? "";
|
|
@@ -7785,7 +7955,8 @@ var RuntimeConfigPlugin = class {
|
|
|
7785
7955
|
const rawApp = httpServer.getRawApp();
|
|
7786
7956
|
const features = {
|
|
7787
7957
|
installLocal: this.installLocal,
|
|
7788
|
-
marketplace: true
|
|
7958
|
+
marketplace: true,
|
|
7959
|
+
aiStudio: this.aiStudio
|
|
7789
7960
|
};
|
|
7790
7961
|
let envRegistry = null;
|
|
7791
7962
|
try {
|
|
@@ -7836,6 +8007,7 @@ var RuntimeConfigPlugin = class {
|
|
|
7836
8007
|
};
|
|
7837
8008
|
this.cloudUrl = config.controlPlaneUrl === "" ? "" : resolveCloudUrl(config.controlPlaneUrl) ?? "";
|
|
7838
8009
|
this.installLocal = !!config.installLocal;
|
|
8010
|
+
this.aiStudio = config.aiStudio !== false;
|
|
7839
8011
|
this.singleEnvironment = !!config.singleEnvironment;
|
|
7840
8012
|
const envName = (typeof process !== "undefined" ? process.env?.OS_PRODUCT_NAME : void 0)?.trim();
|
|
7841
8013
|
const envShort = (typeof process !== "undefined" ? process.env?.OS_PRODUCT_SHORT_NAME : void 0)?.trim();
|
|
@@ -8027,7 +8199,8 @@ var ObjectOSEnvironmentPlugin = class {
|
|
|
8027
8199
|
const factory = new ArtifactKernelFactory({
|
|
8028
8200
|
client,
|
|
8029
8201
|
envRegistry,
|
|
8030
|
-
logger: ctx.logger
|
|
8202
|
+
logger: ctx.logger,
|
|
8203
|
+
defaultRequires: this.config.defaultRequires
|
|
8031
8204
|
});
|
|
8032
8205
|
const kernelManager = new KernelManager({
|
|
8033
8206
|
factory,
|
|
@@ -8084,7 +8257,17 @@ async function createObjectOSStack(config) {
|
|
|
8084
8257
|
};
|
|
8085
8258
|
const enginePlugins = await createHostEnginePlugins();
|
|
8086
8259
|
return {
|
|
8087
|
-
plugins: [
|
|
8260
|
+
plugins: [
|
|
8261
|
+
...enginePlugins,
|
|
8262
|
+
new ObjectOSEnvironmentPlugin(merged),
|
|
8263
|
+
new AuthProxyPlugin(),
|
|
8264
|
+
new MarketplaceProxyPlugin({ controlPlaneUrl: merged.controlPlaneUrl === "file" ? void 0 : merged.controlPlaneUrl }),
|
|
8265
|
+
new RuntimeConfigPlugin({ controlPlaneUrl: merged.controlPlaneUrl === "file" ? void 0 : merged.controlPlaneUrl, installLocal: false }),
|
|
8266
|
+
// Host-supplied product/policy plugins (the official seam — see
|
|
8267
|
+
// ObjectOSStackConfig.extraPlugins). Appended last so they mount
|
|
8268
|
+
// after the framework defaults.
|
|
8269
|
+
...config.extraPlugins ?? []
|
|
8270
|
+
],
|
|
8088
8271
|
api: {
|
|
8089
8272
|
enableProjectScoping: true,
|
|
8090
8273
|
projectResolution: "auto",
|