@jami-studio/core 0.92.27 → 0.92.28
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/corpus/README.md +1 -1
- package/corpus/core/CHANGELOG.md +17 -0
- package/corpus/core/package.json +2 -1
- package/corpus/core/src/deploy/build.ts +56 -2
- package/corpus/core/src/event-bus/bus.ts +138 -140
- package/corpus/core/src/event-bus/registry.ts +81 -79
- package/corpus/core/src/file-upload/registry.ts +135 -125
- package/corpus/core/src/notifications/registry.ts +218 -216
- package/corpus/core/src/private-blob/registry.ts +246 -242
- package/corpus/core/src/secrets/register.ts +132 -129
- package/corpus/core/src/shared/global-scope.ts +75 -0
- package/corpus/core/src/sharing/registry.ts +193 -194
- package/corpus/core/src/tracking/providers.ts +425 -422
- package/corpus/core/src/tracking/registry.ts +102 -102
- package/dist/collab/routes.d.ts +1 -1
- package/dist/deploy/build.d.ts +23 -0
- package/dist/deploy/build.d.ts.map +1 -1
- package/dist/deploy/build.js +36 -2
- package/dist/deploy/build.js.map +1 -1
- package/dist/event-bus/bus.d.ts.map +1 -1
- package/dist/event-bus/bus.js +8 -6
- package/dist/event-bus/bus.js.map +1 -1
- package/dist/event-bus/registry.d.ts.map +1 -1
- package/dist/event-bus/registry.js +10 -6
- package/dist/event-bus/registry.js.map +1 -1
- package/dist/file-upload/actions/upload-image.d.ts +1 -1
- package/dist/file-upload/registry.d.ts.map +1 -1
- package/dist/file-upload/registry.js +28 -8
- package/dist/file-upload/registry.js.map +1 -1
- package/dist/notifications/registry.d.ts.map +1 -1
- package/dist/notifications/registry.js +6 -5
- package/dist/notifications/registry.js.map +1 -1
- package/dist/observability/routes.d.ts +5 -5
- package/dist/private-blob/registry.d.ts.map +1 -1
- package/dist/private-blob/registry.js +18 -13
- package/dist/private-blob/registry.js.map +1 -1
- package/dist/progress/routes.d.ts +1 -1
- package/dist/resources/handlers.d.ts +2 -2
- package/dist/secrets/register.d.ts.map +1 -1
- package/dist/secrets/register.js +11 -7
- package/dist/secrets/register.js.map +1 -1
- package/dist/secrets/routes.d.ts +9 -9
- package/dist/shared/global-scope.d.ts +55 -0
- package/dist/shared/global-scope.d.ts.map +1 -0
- package/dist/shared/global-scope.js +70 -0
- package/dist/shared/global-scope.js.map +1 -0
- package/dist/sharing/registry.d.ts.map +1 -1
- package/dist/sharing/registry.js +2 -16
- package/dist/sharing/registry.js.map +1 -1
- package/dist/tracking/providers.d.ts.map +1 -1
- package/dist/tracking/providers.js +11 -9
- package/dist/tracking/providers.js.map +1 -1
- package/dist/tracking/registry.d.ts.map +1 -1
- package/dist/tracking/registry.js +5 -5
- package/dist/tracking/registry.js.map +1 -1
- package/package.json +2 -1
|
@@ -1,129 +1,132 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* In-process registry of required / optional secrets.
|
|
3
|
-
*
|
|
4
|
-
* Templates call `registerRequiredSecret()` at module load time — typically
|
|
5
|
-
* from a server plugin. The secrets HTTP routes and the sidebar settings UI
|
|
6
|
-
* read from this registry on every request so overrides and late-registered
|
|
7
|
-
* secrets are picked up without a restart.
|
|
8
|
-
*/
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
export
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
/**
|
|
28
|
-
|
|
29
|
-
/**
|
|
30
|
-
|
|
31
|
-
/**
|
|
32
|
-
|
|
33
|
-
/**
|
|
34
|
-
|
|
35
|
-
/**
|
|
36
|
-
|
|
37
|
-
/**
|
|
38
|
-
|
|
39
|
-
/**
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
*
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
//
|
|
60
|
-
//
|
|
61
|
-
//
|
|
62
|
-
//
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
*
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
if (
|
|
82
|
-
secret.
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
1
|
+
/**
|
|
2
|
+
* In-process registry of required / optional secrets.
|
|
3
|
+
*
|
|
4
|
+
* Templates call `registerRequiredSecret()` at module load time — typically
|
|
5
|
+
* from a server plugin. The secrets HTTP routes and the sidebar settings UI
|
|
6
|
+
* read from this registry on every request so overrides and late-registered
|
|
7
|
+
* secrets are picked up without a restart.
|
|
8
|
+
*/
|
|
9
|
+
|
|
10
|
+
import { getScopedGlobal } from "../shared/global-scope.js";
|
|
11
|
+
|
|
12
|
+
export type SecretScope = "user" | "workspace" | "org";
|
|
13
|
+
export type SecretKind = "api-key" | "oauth";
|
|
14
|
+
|
|
15
|
+
export interface ValidatorResult {
|
|
16
|
+
ok: boolean;
|
|
17
|
+
error?: string;
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
export interface SecretValidator {
|
|
21
|
+
(
|
|
22
|
+
value: string,
|
|
23
|
+
): Promise<ValidatorResult | boolean> | ValidatorResult | boolean;
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
export interface RegisteredSecret {
|
|
27
|
+
/** Env var name & settings key — e.g. "OPENAI_API_KEY". */
|
|
28
|
+
key: string;
|
|
29
|
+
/** Human-readable label shown in the sidebar. */
|
|
30
|
+
label: string;
|
|
31
|
+
/** Short description shown below the label. */
|
|
32
|
+
description?: string;
|
|
33
|
+
/** URL where the user can obtain the key or connect the account. */
|
|
34
|
+
docsUrl?: string;
|
|
35
|
+
/** Whether the secret is per-user or shared across a workspace/org. */
|
|
36
|
+
scope: SecretScope;
|
|
37
|
+
/** UI affordance: "api-key" renders an input; "oauth" renders Connect. */
|
|
38
|
+
kind: SecretKind;
|
|
39
|
+
/** When true, an onboarding step is auto-injected for this secret. */
|
|
40
|
+
required?: boolean;
|
|
41
|
+
/**
|
|
42
|
+
* Optional health check. Receives the plain-text value, returns `true` or
|
|
43
|
+
* `{ ok: true }` on success. Returning `{ ok: false, error }` surfaces the
|
|
44
|
+
* error to the UI. Never log the value from inside the validator.
|
|
45
|
+
*/
|
|
46
|
+
validator?: SecretValidator;
|
|
47
|
+
/**
|
|
48
|
+
* For `kind: "oauth"` — the oauth-tokens provider id (e.g. "google") that
|
|
49
|
+
* backs this registration. Used to surface OAuth status in the unified UI.
|
|
50
|
+
*/
|
|
51
|
+
oauthProvider?: string;
|
|
52
|
+
/**
|
|
53
|
+
* For `kind: "oauth"` — URL the Connect button should point at. Typically
|
|
54
|
+
* the framework's `/_agent-native/google/auth-url` or similar.
|
|
55
|
+
*/
|
|
56
|
+
oauthConnectUrl?: string;
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
// Pin the registry to globalThis so templates that load `@agent-native/core`
|
|
60
|
+
// via more than one ESM graph (e.g. dev-mode Vite + Nitro, symlinked
|
|
61
|
+
// node_modules, dist/ vs src/) share a single registry. Without this, a
|
|
62
|
+
// template's `register-secrets.ts` side-effect module may populate one
|
|
63
|
+
// registry instance while the /_agent-native/secrets route reads from
|
|
64
|
+
// another — net effect: the UI sees an empty list.
|
|
65
|
+
// Scope-aware + lazily resolved so unified workspace deployments (all apps in
|
|
66
|
+
// one isolate) keep per-app secret registrations. See shared/global-scope.
|
|
67
|
+
function getSecretsRegistry(): Map<string, RegisteredSecret> {
|
|
68
|
+
return getScopedGlobal(
|
|
69
|
+
"agent-native.secrets.registry",
|
|
70
|
+
() => new Map<string, RegisteredSecret>(),
|
|
71
|
+
);
|
|
72
|
+
}
|
|
73
|
+
|
|
74
|
+
/**
|
|
75
|
+
* Register (or override) a required secret.
|
|
76
|
+
*
|
|
77
|
+
* Subsequent registrations with the same `key` replace the previous
|
|
78
|
+
* definition — later plugins can override framework defaults.
|
|
79
|
+
*/
|
|
80
|
+
export function registerRequiredSecret(secret: RegisteredSecret): void {
|
|
81
|
+
if (!secret || typeof secret.key !== "string" || !secret.key) {
|
|
82
|
+
throw new Error("registerRequiredSecret: secret.key is required");
|
|
83
|
+
}
|
|
84
|
+
if (
|
|
85
|
+
secret.scope !== "user" &&
|
|
86
|
+
secret.scope !== "workspace" &&
|
|
87
|
+
secret.scope !== "org"
|
|
88
|
+
) {
|
|
89
|
+
throw new Error(
|
|
90
|
+
`registerRequiredSecret: secret.scope must be "user", "workspace", or "org" (got "${secret.scope}")`,
|
|
91
|
+
);
|
|
92
|
+
}
|
|
93
|
+
if (secret.kind !== "api-key" && secret.kind !== "oauth") {
|
|
94
|
+
throw new Error(
|
|
95
|
+
`registerRequiredSecret: secret.kind must be "api-key" or "oauth" (got "${secret.kind}")`,
|
|
96
|
+
);
|
|
97
|
+
}
|
|
98
|
+
if (getSecretsRegistry().has(secret.key) && process.env.DEBUG) {
|
|
99
|
+
console.log(
|
|
100
|
+
`[agent-native] Overriding registered secret "${secret.key}" with new registration.`,
|
|
101
|
+
);
|
|
102
|
+
}
|
|
103
|
+
getSecretsRegistry().set(secret.key, secret);
|
|
104
|
+
|
|
105
|
+
// Auto-inject an onboarding step for required secrets. Done via dynamic
|
|
106
|
+
// import to avoid a load-order cycle between register and the onboarding
|
|
107
|
+
// registry during module bootstrap.
|
|
108
|
+
if (secret.required) {
|
|
109
|
+
// Lazy import — resolved synchronously in practice because the module is
|
|
110
|
+
// already loaded once any route handler runs, but tolerate async.
|
|
111
|
+
import("./onboarding.js")
|
|
112
|
+
.then((mod) => mod.maybeRegisterSecretOnboardingStep(secret))
|
|
113
|
+
.catch(() => {
|
|
114
|
+
// Onboarding is optional — never let it block registration.
|
|
115
|
+
});
|
|
116
|
+
}
|
|
117
|
+
}
|
|
118
|
+
|
|
119
|
+
/** Return all registered secrets in registration order. */
|
|
120
|
+
export function listRequiredSecrets(): RegisteredSecret[] {
|
|
121
|
+
return Array.from(getSecretsRegistry().values());
|
|
122
|
+
}
|
|
123
|
+
|
|
124
|
+
/** Look up a single registered secret by key. */
|
|
125
|
+
export function getRequiredSecret(key: string): RegisteredSecret | undefined {
|
|
126
|
+
return getSecretsRegistry().get(key);
|
|
127
|
+
}
|
|
128
|
+
|
|
129
|
+
/** Test helper — clears the registry between runs. */
|
|
130
|
+
export function __resetSecretsRegistry(): void {
|
|
131
|
+
getSecretsRegistry().clear();
|
|
132
|
+
}
|
|
@@ -0,0 +1,75 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Per-module-graph global scope for globalThis-pinned framework registries.
|
|
3
|
+
*
|
|
4
|
+
* Why this exists: several framework registries (file-upload providers,
|
|
5
|
+
* private-blob providers, shareable resources, event bus, notification
|
|
6
|
+
* channels, tracking providers, secrets registry) pin their state on
|
|
7
|
+
* `globalThis` so that multiple ESM graphs of ONE app (Vite dev + Nitro,
|
|
8
|
+
* symlinked node_modules, dist/ vs src/) still share a single registry.
|
|
9
|
+
*
|
|
10
|
+
* On a unified workspace deployment (Cloudflare Pages `_worker.js` dispatcher
|
|
11
|
+
* importing every app's worker into ONE isolate) that pinning goes too far:
|
|
12
|
+
* each app's bundle keeps its own module graph — so module-scope state is
|
|
13
|
+
* correctly per-app — but the `globalThis` pin collapses all apps' registries
|
|
14
|
+
* into one shared map. Real-world failure: an upload POSTed to /assets was
|
|
15
|
+
* served by the clips app's registered S3 provider (wrong object prefix).
|
|
16
|
+
*
|
|
17
|
+
* The fix: each app's generated worker entry calls `setGlobalScopeId(appId)`
|
|
18
|
+
* from a scope-init module evaluated FIRST in the entry's import graph (ESM
|
|
19
|
+
* evaluates imports depth-first in declaration order, so the scope is set
|
|
20
|
+
* before any registry module initializes or registers built-ins). Registries
|
|
21
|
+
* resolve their `globalThis` key LAZILY through `getScopedGlobal`, which
|
|
22
|
+
* namespaces the key by the module graph's scope id. Result:
|
|
23
|
+
*
|
|
24
|
+
* - Unified worker: each app's core copy has its own scope id → per-app keys
|
|
25
|
+
* → per-app registries. Cross-app state sharing is gone.
|
|
26
|
+
* - Dev / single-app deployments: `setGlobalScopeId` is never called → keys
|
|
27
|
+
* are unscoped → the original multi-graph dedupe behavior is preserved.
|
|
28
|
+
*
|
|
29
|
+
* This module must stay dependency-free: it is imported by the generated
|
|
30
|
+
* scope-init module before anything else in the app bundle evaluates.
|
|
31
|
+
*/
|
|
32
|
+
|
|
33
|
+
let moduleGraphScopeId: string | null = null;
|
|
34
|
+
|
|
35
|
+
/**
|
|
36
|
+
* Set the global-registry scope for THIS module graph (this app's bundle).
|
|
37
|
+
* Called by the generated worker entry's scope-init module on unified
|
|
38
|
+
* workspace deployments. Pass `null` to clear (tests).
|
|
39
|
+
*/
|
|
40
|
+
export function setGlobalScopeId(id: string | null): void {
|
|
41
|
+
const trimmed = typeof id === "string" ? id.trim() : "";
|
|
42
|
+
moduleGraphScopeId = trimmed ? trimmed : null;
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
/** The active scope id for this module graph, or `null` when unscoped. */
|
|
46
|
+
export function getGlobalScopeId(): string | null {
|
|
47
|
+
return moduleGraphScopeId;
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
/**
|
|
51
|
+
* The fully-qualified global key name for `base` under the active scope.
|
|
52
|
+
* Unscoped graphs get `base` unchanged, so existing dev-mode behavior
|
|
53
|
+
* (one registry across all of an app's ESM graphs) is preserved.
|
|
54
|
+
*/
|
|
55
|
+
export function scopedGlobalKeyName(base: string): string {
|
|
56
|
+
return moduleGraphScopeId ? `${base}::app:${moduleGraphScopeId}` : base;
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
/**
|
|
60
|
+
* Lazily resolve (and initialize once) a globalThis-pinned singleton under
|
|
61
|
+
* the scope-aware key for `base`. Registries MUST call this per access —
|
|
62
|
+
* never capture the result in module scope — so a scope id set during
|
|
63
|
+
* entry-module evaluation is honored by every later registration and read.
|
|
64
|
+
*/
|
|
65
|
+
export function getScopedGlobal<T>(base: string, init: () => T): T {
|
|
66
|
+
const key = Symbol.for(scopedGlobalKeyName(base));
|
|
67
|
+
const g = globalThis as unknown as Record<symbol, T | undefined>;
|
|
68
|
+
return (g[key] ??= init());
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
/** Test helper — delete the pinned singleton for `base` in the ACTIVE scope. */
|
|
72
|
+
export function __deleteScopedGlobal(base: string): void {
|
|
73
|
+
const key = Symbol.for(scopedGlobalKeyName(base));
|
|
74
|
+
delete (globalThis as unknown as Record<symbol, unknown>)[key];
|
|
75
|
+
}
|