@pyric/cli 0.1.0-alpha.12 → 0.1.0-alpha.13
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/cli/serve.js +4 -4
- package/dist/cli/serve.js.map +1 -1
- package/dist/serve/bundler.d.ts +13 -8
- package/dist/serve/bundler.d.ts.map +1 -1
- package/dist/serve/bundler.js +31 -36
- package/dist/serve/bundler.js.map +1 -1
- package/dist/serve/docs-ui/docs/_rhythm/index.html +1 -1
- package/dist/serve/docs-ui/docs/agent/set-up-your-agent/index.html +1 -1
- package/dist/serve/docs-ui/docs/agent/watch-and-review/index.html +1 -1
- package/dist/serve/docs-ui/docs/agent/work-with-an-agent/index.html +1 -1
- package/dist/serve/docs-ui/docs/ai-compat/index.html +1 -1
- package/dist/serve/docs-ui/docs/api-reference/index.html +1 -1
- package/dist/serve/docs-ui/docs/app-compat/index.html +1 -1
- package/dist/serve/docs-ui/docs/auth-compat/index.html +1 -1
- package/dist/serve/docs-ui/docs/build/ai-logic/index.html +44 -22
- package/dist/serve/docs-ui/docs/build/ai-logic.md +69 -21
- package/dist/serve/docs-ui/docs/build/authentication/index.html +1 -1
- package/dist/serve/docs-ui/docs/build/cloud-firestore/index.html +1 -1
- package/dist/serve/docs-ui/docs/build/cloud-messaging/index.html +1 -1
- package/dist/serve/docs-ui/docs/build/cloud-storage/index.html +1 -1
- package/dist/serve/docs-ui/docs/build/realtime-database/index.html +1 -1
- package/dist/serve/docs-ui/docs/conformance-scores/index.html +1 -1
- package/dist/serve/docs-ui/docs/create-pyric-reference-api/index.html +1 -1
- package/dist/serve/docs-ui/docs/database-compat/index.html +1 -1
- package/dist/serve/docs-ui/docs/firestore-compat/index.html +1 -1
- package/dist/serve/docs-ui/docs/functions-rtdb-compat/index.html +1 -1
- package/dist/serve/docs-ui/docs/get-started/how-the-swap-works/index.html +2 -1
- package/dist/serve/docs-ui/docs/get-started/how-the-swap-works.md +2 -0
- package/dist/serve/docs-ui/docs/get-started/start-building/index.html +3 -2
- package/dist/serve/docs-ui/docs/get-started/start-building.md +3 -1
- package/dist/serve/docs-ui/docs/index.json +149 -4
- package/dist/serve/docs-ui/docs/messaging-compat/index.html +1 -1
- package/dist/serve/docs-ui/docs/observe/resolve-runtime-status/index.html +28 -0
- package/dist/serve/docs-ui/docs/observe/resolve-runtime-status.md +47 -0
- package/dist/serve/docs-ui/docs/observe/see-whats-happening/index.html +1 -1
- package/dist/serve/docs-ui/docs/observe/shape-your-data/index.html +1 -1
- package/dist/serve/docs-ui/docs/overview/index.html +1 -1
- package/dist/serve/docs-ui/docs/pyric-admin-app-reference-api/index.html +1 -1
- package/dist/serve/docs-ui/docs/pyric-admin-auth-reference-api/index.html +1 -1
- package/dist/serve/docs-ui/docs/pyric-admin-database-reference-api/index.html +1 -1
- package/dist/serve/docs-ui/docs/pyric-admin-firestore-reference-api/index.html +1 -1
- package/dist/serve/docs-ui/docs/pyric-admin-messaging-reference-api/index.html +1 -1
- package/dist/serve/docs-ui/docs/pyric-admin-storage-reference-api/index.html +1 -1
- package/dist/serve/docs-ui/docs/pyric-ai-reference-api/index.html +1 -1
- package/dist/serve/docs-ui/docs/pyric-ai-scripting-reference-api/index.html +1 -1
- package/dist/serve/docs-ui/docs/pyric-app-reference-api/index.html +1 -1
- package/dist/serve/docs-ui/docs/pyric-auth-reference-api/index.html +1 -1
- package/dist/serve/docs-ui/docs/pyric-cli-assurance-browser-reference-api/index.html +1 -1
- package/dist/serve/docs-ui/docs/pyric-cli-assurance-reference-api/index.html +1 -1
- package/dist/serve/docs-ui/docs/pyric-cli-bridge-client-reference-api/index.html +1 -1
- package/dist/serve/docs-ui/docs/pyric-cli-bridge-reference-api/index.html +1 -1
- package/dist/serve/docs-ui/docs/pyric-cli-conformance-browser-reference-api/index.html +1 -1
- package/dist/serve/docs-ui/docs/pyric-cli-conformance-docs-reference-api/index.html +1 -1
- package/dist/serve/docs-ui/docs/pyric-cli-conformance-reference-api/index.html +1 -1
- package/dist/serve/docs-ui/docs/pyric-cli-credentials-node-reference-api/index.html +1 -1
- package/dist/serve/docs-ui/docs/pyric-cli-discover-reference-api/index.html +1 -1
- package/dist/serve/docs-ui/docs/pyric-cli-register-reference-api/index.html +1 -1
- package/dist/serve/docs-ui/docs/pyric-cli-remote-reference-api/index.html +1 -1
- package/dist/serve/docs-ui/docs/pyric-cli-serve-worker-reference-api/index.html +38 -11
- package/dist/serve/docs-ui/docs/pyric-cli-serve-worker-reference-api.md +26 -8
- package/dist/serve/docs-ui/docs/pyric-cli-verify-reference-api/index.html +1 -1
- package/dist/serve/docs-ui/docs/pyric-cli-vite-reference-api/index.html +11 -8
- package/dist/serve/docs-ui/docs/pyric-cli-vite-reference-api.md +17 -4
- package/dist/serve/docs-ui/docs/pyric-database-reference-api/index.html +1 -1
- package/dist/serve/docs-ui/docs/pyric-firestore-reference-api/index.html +1 -1
- package/dist/serve/docs-ui/docs/pyric-messaging-reference-api/index.html +1 -1
- package/dist/serve/docs-ui/docs/pyric-messaging-sw-reference-api/index.html +1 -1
- package/dist/serve/docs-ui/docs/pyric-rules-reference-api/index.html +1 -1
- package/dist/serve/docs-ui/docs/pyric-sandbox-database-reference-api/index.html +1 -1
- package/dist/serve/docs-ui/docs/pyric-sandbox-firestore-reference-api/index.html +1 -1
- package/dist/serve/docs-ui/docs/pyric-sandbox-reference-api/index.html +1 -1
- package/dist/serve/docs-ui/docs/pyric-storage-reference-api/index.html +1 -1
- package/dist/serve/docs-ui/docs/pyric-ui-agents-reference-api/index.html +1 -1
- package/dist/serve/docs-ui/docs/pyric-ui-auth-hooks-reference-api/index.html +1 -1
- package/dist/serve/docs-ui/docs/pyric-ui-auth-reference-api/index.html +1 -1
- package/dist/serve/docs-ui/docs/pyric-ui-events-hooks-reference-api/index.html +1 -1
- package/dist/serve/docs-ui/docs/pyric-ui-events-reference-api/index.html +1 -1
- package/dist/serve/docs-ui/docs/pyric-ui-firestore-hooks-reference-api/index.html +1 -1
- package/dist/serve/docs-ui/docs/pyric-ui-firestore-reference-api/index.html +1 -1
- package/dist/serve/docs-ui/docs/pyric-ui-primitives-reference-api/index.html +1 -1
- package/dist/serve/docs-ui/docs/pyric-ui-rtdb-reference-api/index.html +1 -1
- package/dist/serve/docs-ui/docs/pyric-ui-rules-hooks-reference-api/index.html +1 -1
- package/dist/serve/docs-ui/docs/pyric-ui-rules-reference-api/index.html +1 -1
- package/dist/serve/docs-ui/docs/pyric-ui-storage-hooks-reference-api/index.html +1 -1
- package/dist/serve/docs-ui/docs/pyric-ui-storage-reference-api/index.html +1 -1
- package/dist/serve/docs-ui/docs/pyric-ui-traffic-hooks-reference-api/index.html +1 -1
- package/dist/serve/docs-ui/docs/pyric-ui-traffic-reference-api/index.html +1 -1
- package/dist/serve/docs-ui/docs/rules-compat/index.html +1 -1
- package/dist/serve/docs-ui/docs/secure/audit-your-rules/index.html +1 -1
- package/dist/serve/docs-ui/docs/secure/firestore-rules-limits/index.html +1 -1
- package/dist/serve/docs-ui/docs/secure/read-a-denial/index.html +1 -1
- package/dist/serve/docs-ui/docs/secure/rtdb-rules-in-typescript/index.html +1 -1
- package/dist/serve/docs-ui/docs/secure/rules-standard-library/index.html +1 -1
- package/dist/serve/docs-ui/docs/secure/secure-it-with-rules/index.html +1 -1
- package/dist/serve/docs-ui/docs/secure/simulate-and-lint/index.html +1 -1
- package/dist/serve/docs-ui/docs/secure/write-a-rules-test-suite/index.html +1 -1
- package/dist/serve/docs-ui/docs/ship/ship-to-production/index.html +1 -1
- package/dist/serve/docs-ui/docs/ship/test-in-node/index.html +1 -1
- package/dist/serve/docs-ui/docs/storage-compat/index.html +1 -1
- package/dist/serve/docs-ui/docs/trust/how-we-know-it-matches-firebase/index.html +1 -1
- package/dist/serve/docs-ui/docs/trust/versioning-and-compatibility/index.html +1 -1
- package/dist/serve/docs-ui/docs/tutorial/index.html +429 -0
- package/dist/serve/docs-ui/docs/tutorial.md +701 -0
- package/dist/serve/docs-ui/llms.txt +2 -0
- package/dist/serve/entries/init.d.ts.map +1 -1
- package/dist/serve/entries/init.js +3 -0
- package/dist/serve/entries/init.js.map +1 -1
- package/dist/serve/entries/runtime.d.ts.map +1 -1
- package/dist/serve/entries/runtime.js +20 -5
- package/dist/serve/entries/runtime.js.map +1 -1
- package/dist/serve/entries/worker-runtime.d.ts +2 -2
- package/dist/serve/entries/worker-runtime.d.ts.map +1 -1
- package/dist/serve/entries/worker-runtime.js +45 -10
- package/dist/serve/entries/worker-runtime.js.map +1 -1
- package/dist/serve/runtime/chip-config.d.ts +19 -0
- package/dist/serve/runtime/chip-config.d.ts.map +1 -0
- package/dist/serve/runtime/chip-config.js +19 -0
- package/dist/serve/runtime/chip-config.js.map +1 -0
- package/dist/serve/runtime/chip-install.d.ts +10 -0
- package/dist/serve/runtime/chip-install.d.ts.map +1 -0
- package/dist/serve/runtime/chip-install.js +15 -0
- package/dist/serve/runtime/chip-install.js.map +1 -0
- package/dist/serve/runtime/chip.d.ts +17 -0
- package/dist/serve/runtime/chip.d.ts.map +1 -0
- package/dist/serve/runtime/chip.js +257 -0
- package/dist/serve/runtime/chip.js.map +1 -0
- package/dist/serve/runtime/manifest.d.ts +22 -0
- package/dist/serve/runtime/manifest.d.ts.map +1 -0
- package/dist/serve/runtime/manifest.js +20 -0
- package/dist/serve/runtime/manifest.js.map +1 -0
- package/dist/serve/runtime/status.d.ts +49 -0
- package/dist/serve/runtime/status.d.ts.map +1 -0
- package/dist/serve/runtime/status.js +164 -0
- package/dist/serve/runtime/status.js.map +1 -0
- package/dist/serve/runtime/worker-connection.d.ts +4 -0
- package/dist/serve/runtime/worker-connection.d.ts.map +1 -0
- package/dist/serve/runtime/worker-connection.js +11 -0
- package/dist/serve/runtime/worker-connection.js.map +1 -0
- package/dist/serve/runtime/worker-generation.d.ts +14 -0
- package/dist/serve/runtime/worker-generation.d.ts.map +1 -0
- package/dist/serve/runtime/worker-generation.js +56 -0
- package/dist/serve/runtime/worker-generation.js.map +1 -0
- package/dist/serve/runtime/worker-replacement.d.ts +18 -0
- package/dist/serve/runtime/worker-replacement.d.ts.map +1 -0
- package/dist/serve/runtime/worker-replacement.js +29 -0
- package/dist/serve/runtime/worker-replacement.js.map +1 -0
- package/dist/serve/standalone-assets.d.ts +2 -2
- package/dist/serve/standalone-assets.d.ts.map +1 -1
- package/dist/serve/standalone-assets.js +1 -1
- package/dist/serve/standalone-assets.js.map +1 -1
- package/dist/serve/studio-ui/assets/{RulesCodeEditor-CxVYcZXw.js → RulesCodeEditor-BDlH-5lp.js} +1 -1
- package/dist/serve/studio-ui/assets/{index-cpSfgQ7H.js → index-Bf9SSE8L.js} +5 -5
- package/dist/serve/studio-ui/assets/{seed-DeuFKi4J.js → seed-D_UjxfOU.js} +1 -1
- package/dist/serve/studio-ui/index.html +1 -1
- package/dist/serve/vite-ai-config.d.ts +25 -0
- package/dist/serve/vite-ai-config.d.ts.map +1 -0
- package/dist/serve/vite-ai-config.js +45 -0
- package/dist/serve/vite-ai-config.js.map +1 -0
- package/dist/serve/vite-plugin.d.ts +17 -36
- package/dist/serve/vite-plugin.d.ts.map +1 -1
- package/dist/serve/vite-plugin.js +38 -57
- package/dist/serve/vite-plugin.js.map +1 -1
- package/dist/serve/vite-rules-source.d.ts +8 -0
- package/dist/serve/vite-rules-source.d.ts.map +1 -0
- package/dist/serve/vite-rules-source.js +23 -0
- package/dist/serve/vite-rules-source.js.map +1 -0
- package/dist/serve/vite-worker-runtime.d.ts +20 -0
- package/dist/serve/vite-worker-runtime.d.ts.map +1 -0
- package/dist/serve/vite-worker-runtime.js +25 -0
- package/dist/serve/vite-worker-runtime.js.map +1 -0
- package/dist/serve/worker/client/connection.d.ts +7 -2
- package/dist/serve/worker/client/connection.d.ts.map +1 -1
- package/dist/serve/worker/client/connection.js +13 -16
- package/dist/serve/worker/client/connection.js.map +1 -1
- package/dist/serve/worker/client/core.d.ts +7 -0
- package/dist/serve/worker/client/core.d.ts.map +1 -1
- package/dist/serve/worker/client/core.js +28 -0
- package/dist/serve/worker/client/core.js.map +1 -1
- package/dist/serve/worker/client/runtime-control.d.ts +8 -0
- package/dist/serve/worker/client/runtime-control.d.ts.map +1 -0
- package/dist/serve/worker/client/runtime-control.js +11 -0
- package/dist/serve/worker/client/runtime-control.js.map +1 -0
- package/dist/serve/worker/entry.js +44 -2
- package/dist/serve/worker/entry.js.map +1 -1
- package/dist/serve/worker/host/studio.js +1 -1
- package/dist/serve/worker/host/studio.js.map +1 -1
- package/dist/serve/worker/host-context.d.ts +1 -1
- package/dist/serve/worker/host-context.d.ts.map +1 -1
- package/dist/serve/worker/protocol.d.ts +15 -1
- package/dist/serve/worker/protocol.d.ts.map +1 -1
- package/dist/serve/worker/protocol.js.map +1 -1
- package/dist/serve/worker/retirement.d.ts +21 -0
- package/dist/serve/worker/retirement.d.ts.map +1 -0
- package/dist/serve/worker/retirement.js +97 -0
- package/dist/serve/worker/retirement.js.map +1 -0
- package/dist/serve/worker/serve-init.d.ts.map +1 -1
- package/dist/serve/worker/serve-init.js +5 -6
- package/dist/serve/worker/serve-init.js.map +1 -1
- package/dist/vite.d.ts +1 -0
- package/dist/vite.d.ts.map +1 -1
- package/dist/vite.js.map +1 -1
- package/package.json +5 -4
- package/src/cli/serve.ts +4 -4
- package/src/serve/bundler.ts +41 -15
- package/src/serve/entries/init.ts +3 -0
- package/src/serve/entries/runtime.ts +23 -7
- package/src/serve/entries/worker-runtime.ts +69 -9
- package/src/serve/runtime/chip-config.ts +30 -0
- package/src/serve/runtime/chip-install.ts +27 -0
- package/src/serve/runtime/chip.ts +282 -0
- package/src/serve/runtime/manifest.ts +37 -0
- package/src/serve/runtime/status.ts +216 -0
- package/src/serve/runtime/worker-connection.ts +14 -0
- package/src/serve/runtime/worker-generation.ts +75 -0
- package/src/serve/runtime/worker-replacement.ts +42 -0
- package/src/serve/standalone-assets.ts +2 -2
- package/src/serve/vite-ai-config.ts +77 -0
- package/src/serve/vite-plugin.ts +60 -87
- package/src/serve/vite-rules-source.ts +28 -0
- package/src/serve/vite-worker-runtime.ts +51 -0
- package/src/serve/worker/client/connection.ts +29 -4
- package/src/serve/worker/client/core.ts +34 -0
- package/src/serve/worker/client/runtime-control.ts +22 -0
- package/src/serve/worker/entry.ts +46 -2
- package/src/serve/worker/host/studio.ts +1 -1
- package/src/serve/worker/host-context.ts +1 -1
- package/src/serve/worker/protocol.ts +13 -1
- package/src/serve/worker/retirement.ts +125 -0
- package/src/serve/worker/serve-init.ts +5 -6
- package/src/vite.ts +1 -0
|
@@ -0,0 +1,216 @@
|
|
|
1
|
+
import type { SandboxEvent } from 'pyric/sandbox';
|
|
2
|
+
import type { PyricRuntimeManifest } from './manifest.js';
|
|
3
|
+
import { readPyricRuntimeManifest } from './manifest.js';
|
|
4
|
+
|
|
5
|
+
export type PyricRuntimeMode = 'starting' | 'shared-worker' | 'in-page';
|
|
6
|
+
export type PyricRuntimeErrorSource = 'sandbox' | 'worker' | 'runtime';
|
|
7
|
+
|
|
8
|
+
export interface PyricRuntimeError {
|
|
9
|
+
id: string;
|
|
10
|
+
source: PyricRuntimeErrorSource;
|
|
11
|
+
at: number;
|
|
12
|
+
message: string;
|
|
13
|
+
code?: string;
|
|
14
|
+
stack?: string;
|
|
15
|
+
service?: string;
|
|
16
|
+
method?: string;
|
|
17
|
+
path?: string;
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
export interface PyricRuntimeSnapshot {
|
|
21
|
+
manifest: PyricRuntimeManifest;
|
|
22
|
+
mode: PyricRuntimeMode;
|
|
23
|
+
servedEpoch: string | null;
|
|
24
|
+
runningEpoch: string | null;
|
|
25
|
+
updateAvailable: boolean;
|
|
26
|
+
updatingWorker: boolean;
|
|
27
|
+
errors: readonly PyricRuntimeError[];
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
export interface PyricRuntimeStatus {
|
|
31
|
+
getSnapshot(): PyricRuntimeSnapshot;
|
|
32
|
+
subscribe(listener: (snapshot: PyricRuntimeSnapshot) => void): () => void;
|
|
33
|
+
setWorker(input: { mode: Exclude<PyricRuntimeMode, 'starting'>; runningEpoch?: string | null }): void;
|
|
34
|
+
setWorkerUpdater(update: (() => Promise<void>) | null): void;
|
|
35
|
+
updateWorker(): Promise<void>;
|
|
36
|
+
reportError(error: unknown, source: PyricRuntimeErrorSource): void;
|
|
37
|
+
recordSandboxEvents(events: readonly SandboxEvent[]): void;
|
|
38
|
+
clearErrors(): void;
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
let nextErrorId = 0;
|
|
42
|
+
|
|
43
|
+
function errorCode(value: unknown): string | undefined {
|
|
44
|
+
if (!value || typeof value !== 'object') return undefined;
|
|
45
|
+
const code = (value as { code?: unknown }).code;
|
|
46
|
+
return typeof code === 'string' ? code : undefined;
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
/** Convert thrown values into a serializable, copyable runtime error. */
|
|
50
|
+
export function normalizePyricRuntimeError(
|
|
51
|
+
value: unknown,
|
|
52
|
+
source: PyricRuntimeErrorSource,
|
|
53
|
+
at = Date.now(),
|
|
54
|
+
id = `runtime-error-${++nextErrorId}`,
|
|
55
|
+
): PyricRuntimeError {
|
|
56
|
+
if (value instanceof Error) {
|
|
57
|
+
return {
|
|
58
|
+
id,
|
|
59
|
+
source,
|
|
60
|
+
at,
|
|
61
|
+
message: value.message,
|
|
62
|
+
...(errorCode(value) ? { code: errorCode(value) } : {}),
|
|
63
|
+
...(value.stack ? { stack: value.stack } : {}),
|
|
64
|
+
};
|
|
65
|
+
}
|
|
66
|
+
if (typeof value === 'string') return { id, source, at, message: value };
|
|
67
|
+
if (value && typeof value === 'object') {
|
|
68
|
+
const message = (value as { message?: unknown }).message;
|
|
69
|
+
return {
|
|
70
|
+
id,
|
|
71
|
+
source,
|
|
72
|
+
at,
|
|
73
|
+
message: typeof message === 'string' ? message : String(value),
|
|
74
|
+
...(errorCode(value) ? { code: errorCode(value) } : {}),
|
|
75
|
+
};
|
|
76
|
+
}
|
|
77
|
+
return { id, source, at, message: String(value) };
|
|
78
|
+
}
|
|
79
|
+
|
|
80
|
+
function sandboxError(event: SandboxEvent): PyricRuntimeError | null {
|
|
81
|
+
if (event.kind === 'runtime_error') {
|
|
82
|
+
return {
|
|
83
|
+
id: event.id,
|
|
84
|
+
source: 'sandbox',
|
|
85
|
+
at: event.at,
|
|
86
|
+
service: event.service,
|
|
87
|
+
method: event.method,
|
|
88
|
+
...(event.path ? { path: event.path } : {}),
|
|
89
|
+
...(event.error.code ? { code: event.error.code } : {}),
|
|
90
|
+
message: event.error.message,
|
|
91
|
+
};
|
|
92
|
+
}
|
|
93
|
+
if (event.kind === 'listener_errored' && event.error) {
|
|
94
|
+
return {
|
|
95
|
+
id: event.id,
|
|
96
|
+
source: 'sandbox',
|
|
97
|
+
at: event.at,
|
|
98
|
+
service: 'firestore',
|
|
99
|
+
method: 'listener',
|
|
100
|
+
...('path' in event.target ? { path: event.target.path } : {}),
|
|
101
|
+
code: event.error.code,
|
|
102
|
+
message: event.error.message,
|
|
103
|
+
};
|
|
104
|
+
}
|
|
105
|
+
if (event.kind === 'listener' && event.phase === 'errored' && event.error) {
|
|
106
|
+
return {
|
|
107
|
+
id: event.id,
|
|
108
|
+
source: 'sandbox',
|
|
109
|
+
at: event.at,
|
|
110
|
+
service: event.service,
|
|
111
|
+
method: 'listener',
|
|
112
|
+
...(event.target.path ? { path: event.target.path } : {}),
|
|
113
|
+
...(event.error.code ? { code: event.error.code } : {}),
|
|
114
|
+
message: event.error.message,
|
|
115
|
+
};
|
|
116
|
+
}
|
|
117
|
+
return null;
|
|
118
|
+
}
|
|
119
|
+
|
|
120
|
+
/** Observable, bounded status model shared by the runtime and injected UI. */
|
|
121
|
+
export function createPyricRuntimeStatus(
|
|
122
|
+
manifest: PyricRuntimeManifest,
|
|
123
|
+
options: { maxErrors?: number } = {},
|
|
124
|
+
): PyricRuntimeStatus {
|
|
125
|
+
const maxErrors = Math.max(1, options.maxErrors ?? 50);
|
|
126
|
+
const listeners = new Set<(snapshot: PyricRuntimeSnapshot) => void>();
|
|
127
|
+
const errorIds = new Set<string>();
|
|
128
|
+
let snapshot: PyricRuntimeSnapshot = {
|
|
129
|
+
manifest,
|
|
130
|
+
mode: 'starting',
|
|
131
|
+
servedEpoch: manifest.worker.servedEpoch,
|
|
132
|
+
runningEpoch: null,
|
|
133
|
+
updateAvailable: false,
|
|
134
|
+
updatingWorker: false,
|
|
135
|
+
errors: [],
|
|
136
|
+
};
|
|
137
|
+
let workerUpdater: (() => Promise<void>) | null = null;
|
|
138
|
+
|
|
139
|
+
const publish = (next: PyricRuntimeSnapshot): void => {
|
|
140
|
+
snapshot = next;
|
|
141
|
+
for (const listener of listeners) listener(snapshot);
|
|
142
|
+
};
|
|
143
|
+
const appendError = (error: PyricRuntimeError): void => {
|
|
144
|
+
if (errorIds.has(error.id)) return;
|
|
145
|
+
errorIds.add(error.id);
|
|
146
|
+
const errors = [...snapshot.errors, error].slice(-maxErrors);
|
|
147
|
+
const retained = new Set(errors.map((item) => item.id));
|
|
148
|
+
for (const id of errorIds) if (!retained.has(id)) errorIds.delete(id);
|
|
149
|
+
publish({ ...snapshot, errors });
|
|
150
|
+
};
|
|
151
|
+
|
|
152
|
+
return {
|
|
153
|
+
getSnapshot: () => snapshot,
|
|
154
|
+
subscribe(listener) {
|
|
155
|
+
listeners.add(listener);
|
|
156
|
+
listener(snapshot);
|
|
157
|
+
return () => listeners.delete(listener);
|
|
158
|
+
},
|
|
159
|
+
setWorker({ mode, runningEpoch = null }) {
|
|
160
|
+
const servedEpoch = manifest.worker.servedEpoch;
|
|
161
|
+
publish({
|
|
162
|
+
...snapshot,
|
|
163
|
+
mode,
|
|
164
|
+
runningEpoch,
|
|
165
|
+
updateAvailable: Boolean(
|
|
166
|
+
mode === 'shared-worker'
|
|
167
|
+
&& servedEpoch
|
|
168
|
+
&& runningEpoch
|
|
169
|
+
&& runningEpoch !== 'dev'
|
|
170
|
+
&& servedEpoch !== runningEpoch,
|
|
171
|
+
),
|
|
172
|
+
updatingWorker: false,
|
|
173
|
+
});
|
|
174
|
+
},
|
|
175
|
+
setWorkerUpdater(update) {
|
|
176
|
+
workerUpdater = update;
|
|
177
|
+
},
|
|
178
|
+
async updateWorker() {
|
|
179
|
+
if (!snapshot.updateAvailable || snapshot.updatingWorker || !workerUpdater) {
|
|
180
|
+
throw new Error('No Pyric worker update is available.');
|
|
181
|
+
}
|
|
182
|
+
publish({ ...snapshot, updatingWorker: true });
|
|
183
|
+
try {
|
|
184
|
+
await workerUpdater();
|
|
185
|
+
} catch (error) {
|
|
186
|
+
publish({ ...snapshot, updatingWorker: false });
|
|
187
|
+
appendError(normalizePyricRuntimeError(error, 'worker'));
|
|
188
|
+
throw error;
|
|
189
|
+
}
|
|
190
|
+
},
|
|
191
|
+
reportError(error, source) {
|
|
192
|
+
appendError(normalizePyricRuntimeError(error, source));
|
|
193
|
+
},
|
|
194
|
+
recordSandboxEvents(events) {
|
|
195
|
+
for (const event of events) {
|
|
196
|
+
const normalized = sandboxError(event);
|
|
197
|
+
if (normalized) appendError(normalized);
|
|
198
|
+
}
|
|
199
|
+
},
|
|
200
|
+
clearErrors() {
|
|
201
|
+
errorIds.clear();
|
|
202
|
+
publish({ ...snapshot, errors: [] });
|
|
203
|
+
},
|
|
204
|
+
};
|
|
205
|
+
}
|
|
206
|
+
|
|
207
|
+
declare global {
|
|
208
|
+
// eslint-disable-next-line no-var
|
|
209
|
+
var __pyricRuntime: PyricRuntimeStatus | undefined;
|
|
210
|
+
}
|
|
211
|
+
|
|
212
|
+
/** One global status source even when the runtime and UI are separate bundles. */
|
|
213
|
+
export function getPyricRuntimeStatus(): PyricRuntimeStatus {
|
|
214
|
+
return globalThis.__pyricRuntime
|
|
215
|
+
??= createPyricRuntimeStatus(readPyricRuntimeManifest());
|
|
216
|
+
}
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
import type { ClientDb } from '../worker/client/handles.js';
|
|
2
|
+
|
|
3
|
+
/** Construct the control connection without letting a browser policy failure abort runtime boot. */
|
|
4
|
+
export function connectRuntimeWorker(
|
|
5
|
+
connect: () => ClientDb,
|
|
6
|
+
reportError: (error: unknown) => void,
|
|
7
|
+
): ClientDb | null {
|
|
8
|
+
try {
|
|
9
|
+
return connect();
|
|
10
|
+
} catch (error) {
|
|
11
|
+
reportError(error);
|
|
12
|
+
return null;
|
|
13
|
+
}
|
|
14
|
+
}
|
|
@@ -0,0 +1,75 @@
|
|
|
1
|
+
import { PYRIC_WORKER_NAME } from './manifest.js';
|
|
2
|
+
|
|
3
|
+
export const PYRIC_WORKER_GENERATION_KEY = 'pyric:worker-generation';
|
|
4
|
+
const PYRIC_WORKER_GENERATION_PROBE_KEY = 'pyric:worker-generation-probe';
|
|
5
|
+
|
|
6
|
+
interface EpochStorage {
|
|
7
|
+
getItem(key: string): string | null;
|
|
8
|
+
setItem(key: string, value: string): void;
|
|
9
|
+
removeItem?(key: string): void;
|
|
10
|
+
}
|
|
11
|
+
|
|
12
|
+
/** Select the replacement generation only after this page was told to move. */
|
|
13
|
+
export function workerNameForEpoch(
|
|
14
|
+
_servedEpoch: string | null,
|
|
15
|
+
storage: EpochStorage | undefined,
|
|
16
|
+
): string {
|
|
17
|
+
if (!storage) return PYRIC_WORKER_NAME;
|
|
18
|
+
try {
|
|
19
|
+
const generation = storage.getItem(PYRIC_WORKER_GENERATION_KEY);
|
|
20
|
+
return generation && /^[a-f0-9]{16}$/.test(generation)
|
|
21
|
+
? `${PYRIC_WORKER_NAME}:${generation}`
|
|
22
|
+
: PYRIC_WORKER_NAME;
|
|
23
|
+
} catch {
|
|
24
|
+
return PYRIC_WORKER_NAME;
|
|
25
|
+
}
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
/** Persist the generation boundary before a page reloads. */
|
|
29
|
+
export function rememberWorkerEpoch(
|
|
30
|
+
epoch: string,
|
|
31
|
+
storage: EpochStorage | undefined,
|
|
32
|
+
): void {
|
|
33
|
+
if (!/^[a-f0-9]{16}$/.test(epoch)) {
|
|
34
|
+
throw new Error('Pyric cannot replace the worker: invalid served worker epoch.');
|
|
35
|
+
}
|
|
36
|
+
if (!storage) {
|
|
37
|
+
throw new Error(
|
|
38
|
+
'Pyric cannot replace the worker because origin storage is unavailable. Close all tabs for this origin and reopen the app.',
|
|
39
|
+
);
|
|
40
|
+
}
|
|
41
|
+
try {
|
|
42
|
+
storage.setItem(PYRIC_WORKER_GENERATION_KEY, epoch);
|
|
43
|
+
if (storage.getItem(PYRIC_WORKER_GENERATION_KEY) !== epoch) {
|
|
44
|
+
throw new Error('the worker generation could not be read back');
|
|
45
|
+
}
|
|
46
|
+
} catch (cause) {
|
|
47
|
+
throw new Error(
|
|
48
|
+
'Pyric cannot replace the worker because origin storage is unavailable. Close all tabs for this origin and reopen the app.',
|
|
49
|
+
{ cause },
|
|
50
|
+
);
|
|
51
|
+
}
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
/** Prove origin storage works without publishing a successor generation. */
|
|
55
|
+
export function preflightWorkerEpochStorage(storage: EpochStorage | undefined): void {
|
|
56
|
+
if (!storage?.removeItem) {
|
|
57
|
+
throw new Error(
|
|
58
|
+
'Pyric cannot replace the worker because origin storage is unavailable. Close all tabs for this origin and reopen the app.',
|
|
59
|
+
);
|
|
60
|
+
}
|
|
61
|
+
try {
|
|
62
|
+
const previousProbe = storage.getItem(PYRIC_WORKER_GENERATION_PROBE_KEY);
|
|
63
|
+
storage.setItem(PYRIC_WORKER_GENERATION_PROBE_KEY, 'ok');
|
|
64
|
+
if (storage.getItem(PYRIC_WORKER_GENERATION_PROBE_KEY) !== 'ok') {
|
|
65
|
+
throw new Error('the worker generation probe could not be read back');
|
|
66
|
+
}
|
|
67
|
+
if (previousProbe === null) storage.removeItem(PYRIC_WORKER_GENERATION_PROBE_KEY);
|
|
68
|
+
else storage.setItem(PYRIC_WORKER_GENERATION_PROBE_KEY, previousProbe);
|
|
69
|
+
} catch (cause) {
|
|
70
|
+
throw new Error(
|
|
71
|
+
'Pyric cannot replace the worker because origin storage is unavailable. Close all tabs for this origin and reopen the app.',
|
|
72
|
+
{ cause },
|
|
73
|
+
);
|
|
74
|
+
}
|
|
75
|
+
}
|
|
@@ -0,0 +1,42 @@
|
|
|
1
|
+
interface WorkerReplacementOptions {
|
|
2
|
+
targetEpoch: string;
|
|
3
|
+
retire(): Promise<void>;
|
|
4
|
+
subscribeReload(listener: (epoch: string) => void): () => void;
|
|
5
|
+
preflight(): void;
|
|
6
|
+
commitGeneration(epoch: string): void;
|
|
7
|
+
onPreparationError?(error: unknown): void;
|
|
8
|
+
reload(): void;
|
|
9
|
+
schedule?: (run: () => void) => void;
|
|
10
|
+
}
|
|
11
|
+
|
|
12
|
+
export interface WorkerReplacement {
|
|
13
|
+
request(): Promise<void>;
|
|
14
|
+
dispose(): void;
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
/** Coordinate one reload per page after the old SharedWorker announces retirement. */
|
|
18
|
+
export function createWorkerReplacement(options: WorkerReplacementOptions): WorkerReplacement {
|
|
19
|
+
const schedule = options.schedule ?? ((run) => { setTimeout(run, 100); });
|
|
20
|
+
let reloadScheduled = false;
|
|
21
|
+
const unsubscribe = options.subscribeReload((epoch) => {
|
|
22
|
+
if (reloadScheduled) return;
|
|
23
|
+
try {
|
|
24
|
+
options.commitGeneration(epoch);
|
|
25
|
+
} catch (error) {
|
|
26
|
+
options.onPreparationError?.(error);
|
|
27
|
+
return;
|
|
28
|
+
}
|
|
29
|
+
reloadScheduled = true;
|
|
30
|
+
schedule(options.reload);
|
|
31
|
+
});
|
|
32
|
+
return {
|
|
33
|
+
async request() {
|
|
34
|
+
// Verify that this page can persist the successor before asking the old
|
|
35
|
+
// worker to retire, but do not publish that successor until the worker
|
|
36
|
+
// announces that its drain and capture barrier completed.
|
|
37
|
+
options.preflight();
|
|
38
|
+
await options.retire();
|
|
39
|
+
},
|
|
40
|
+
dispose: unsubscribe,
|
|
41
|
+
};
|
|
42
|
+
}
|
|
@@ -31,7 +31,7 @@ export interface EmbeddedAssets {
|
|
|
31
31
|
* materialization cache. Optional so older compiled binaries still boot.
|
|
32
32
|
*/
|
|
33
33
|
assetVersion?: string;
|
|
34
|
-
/** Worker
|
|
34
|
+
/** Worker executable epoch stamped into the page (`<meta name="pyric-worker-v">`). */
|
|
35
35
|
workerVersion: string;
|
|
36
36
|
/** Lazy: flat map of SDK filename -> base64 bytes (app.js, worker.js, chunks). */
|
|
37
37
|
sdk: () => Promise<Record<string, string>>;
|
|
@@ -68,7 +68,7 @@ function embedded(): EmbeddedAssets {
|
|
|
68
68
|
return e;
|
|
69
69
|
}
|
|
70
70
|
|
|
71
|
-
/** The worker
|
|
71
|
+
/** The worker executable epoch baked in at compile time (see serve.ts). */
|
|
72
72
|
export function embeddedWorkerVersion(): string {
|
|
73
73
|
return embedded().workerVersion;
|
|
74
74
|
}
|
|
@@ -0,0 +1,77 @@
|
|
|
1
|
+
import type { AIOptions } from 'pyric/ai';
|
|
2
|
+
import path from 'node:path';
|
|
3
|
+
import { loadEnv } from 'vite';
|
|
4
|
+
import type { AiEngineConfigWire } from './worker/protocol.js';
|
|
5
|
+
|
|
6
|
+
const AI_PROXY_PATH = '/__pyric/ai-proxy';
|
|
7
|
+
|
|
8
|
+
/** Declarative AI engines that can cross Vite's page/SharedWorker boundary. */
|
|
9
|
+
export type PyricAiEngineConfig = Extract<NonNullable<AIOptions['engine']>, { kind: string }>;
|
|
10
|
+
|
|
11
|
+
export interface PyricAiOptions {
|
|
12
|
+
/** Simple OpenAI-compatible model selection through Pyric's same-origin proxy. */
|
|
13
|
+
model?: string;
|
|
14
|
+
/** Advanced declarative engine configuration. */
|
|
15
|
+
engine?: PyricAiEngineConfig;
|
|
16
|
+
/** OpenAI-compatible upstream used by the same-origin proxy. */
|
|
17
|
+
proxyUpstream?: string;
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
export interface ResolvedViteAiConfig {
|
|
21
|
+
engineWire: AiEngineConfigWire | undefined;
|
|
22
|
+
proxyUpstream: string | undefined;
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
/** Load env using Vite's `envDir`-relative-to-root convention. */
|
|
26
|
+
export function loadViteAiEnv(
|
|
27
|
+
mode: string,
|
|
28
|
+
root: string | undefined,
|
|
29
|
+
envDir: string | undefined,
|
|
30
|
+
): Record<string, string> {
|
|
31
|
+
const resolvedRoot = path.resolve(root ?? process.cwd());
|
|
32
|
+
const resolvedEnvDir = envDir === undefined
|
|
33
|
+
? resolvedRoot
|
|
34
|
+
: path.resolve(resolvedRoot, envDir);
|
|
35
|
+
return loadEnv(mode, resolvedEnvDir, '');
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
/** Convert a public declarative engine into the JSON-safe worker wire shape. */
|
|
39
|
+
export function engineConfigToWire(engine: PyricAiEngineConfig): AiEngineConfigWire {
|
|
40
|
+
if (engine.kind === 'openai') {
|
|
41
|
+
return {
|
|
42
|
+
kind: 'openai',
|
|
43
|
+
baseUrl: engine.baseUrl ?? AI_PROXY_PATH,
|
|
44
|
+
...(engine.model !== undefined ? { model: engine.model } : {}),
|
|
45
|
+
...(engine.modelMap !== undefined ? { modelMap: engine.modelMap } : {}),
|
|
46
|
+
};
|
|
47
|
+
}
|
|
48
|
+
return {
|
|
49
|
+
kind: 'scripted',
|
|
50
|
+
...(engine.script !== undefined
|
|
51
|
+
? { script: engine.script as unknown as Array<Record<string, unknown>> }
|
|
52
|
+
: {}),
|
|
53
|
+
};
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
/** Resolve the plugin's explicit-options-over-Vite-env AI convention. */
|
|
57
|
+
export function resolveViteAiConfig(
|
|
58
|
+
options: PyricAiOptions | undefined,
|
|
59
|
+
env: Record<string, string | undefined>,
|
|
60
|
+
): ResolvedViteAiConfig {
|
|
61
|
+
if (options?.model !== undefined && options.engine !== undefined) {
|
|
62
|
+
throw new Error('@pyric/cli/vite: Choose either ai.model or ai.engine, not both.');
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
const model = options?.model?.trim() || env.PYRIC_AI_MODEL?.trim();
|
|
66
|
+
const engineWire = options?.engine
|
|
67
|
+
? engineConfigToWire(options.engine)
|
|
68
|
+
: model
|
|
69
|
+
? engineConfigToWire({ kind: 'openai', baseUrl: AI_PROXY_PATH, model })
|
|
70
|
+
: undefined;
|
|
71
|
+
|
|
72
|
+
return {
|
|
73
|
+
engineWire,
|
|
74
|
+
proxyUpstream:
|
|
75
|
+
options?.proxyUpstream?.trim() || env.PYRIC_AI_PROXY_UPSTREAM?.trim() || undefined,
|
|
76
|
+
};
|
|
77
|
+
}
|