@tealbrick/kit 0.3.0-rc.4 → 0.3.0-rc.5
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/BOOTSTRAP.md +3 -3
- package/NATIVE.md +2 -2
- package/README.md +10 -2
- package/dist/native-runtime-sync.d.ts +49 -0
- package/dist/native-runtime-sync.js +101 -0
- package/dist/native-serve.d.ts +19 -0
- package/dist/native-serve.js +5 -1
- package/dist/secrets.d.ts +2 -1
- package/dist/secrets.js +4 -2
- package/package.json +6 -6
package/BOOTSTRAP.md
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
> Prerelease 0.3.0-rc.
|
|
1
|
+
> Prerelease 0.3.0-rc.5 targets fresh Eve 0.70 installations. Use the explicit candidate version or `next` tag after publication. Scoped `@tealbrick/*` `latest` remains 0.2.7 for existing Eve 0.66 installations; durable cross-version session migration is not validated.
|
|
2
2
|
|
|
3
3
|
# Deployment guide: Eve agent + Teal Brick kit
|
|
4
4
|
|
|
@@ -19,7 +19,7 @@ For a fresh machine, the sequence is:
|
|
|
19
19
|
|
|
20
20
|
1. Install Node 24 and npm.
|
|
21
21
|
2. Create an Eve 0.70.0 project and configure its primary model credentials.
|
|
22
|
-
3. Install `@tealbrick/kit@0.3.0-rc.
|
|
22
|
+
3. Install `@tealbrick/kit@0.3.0-rc.5` inside that project.
|
|
23
23
|
4. Run `tealbrick setup` to sign in, select capabilities and register the card.
|
|
24
24
|
5. Build and start Eve, then check a real chat from desktop.
|
|
25
25
|
|
|
@@ -81,7 +81,7 @@ Run this inside the Eve project created in step 2 (or your existing compatible
|
|
|
81
81
|
project), not in an empty directory or as a global install:
|
|
82
82
|
|
|
83
83
|
```bash
|
|
84
|
-
npm install --save-exact @tealbrick/kit@0.3.0-rc.
|
|
84
|
+
npm install --save-exact @tealbrick/kit@0.3.0-rc.5
|
|
85
85
|
```
|
|
86
86
|
|
|
87
87
|
This installs Portal, AVM, Voice, Vision, Deliver and the shared provider transport
|
package/NATIVE.md
CHANGED
|
@@ -1,10 +1,10 @@
|
|
|
1
1
|
# Native agent setup — Stage A candidate
|
|
2
2
|
|
|
3
|
-
Prerelease suite **0.3.0-rc.
|
|
3
|
+
Prerelease suite **0.3.0-rc.5** targets the `next` tag. The seven scoped `@tealbrick/*` packages keep `latest` at 0.2.7, which lacks this flow; the unscoped `tealbrick` facade keeps `latest` at 0.3.0-rc.1. The `tealbrick` facade forwards the existing kit CLI; it is not another SDK. Matching Portal runtime setup endpoints must be deployed before native onboarding can succeed. RC4 added `tealbrick native serve` and `native enroll` (see README.md). RC5 keeps an idle serve process's runtime acknowledged in Portal, and lets the runtime reach Portal-provisioned Knowledge with short-lived Portal grants when no local app binding exists.
|
|
4
4
|
|
|
5
5
|
## Install and connect
|
|
6
6
|
|
|
7
|
-
Install with `npm install --save-exact tealbrick@0.3.0-rc.
|
|
7
|
+
Install with `npm install --save-exact tealbrick@0.3.0-rc.5` (or `tealbrick@next`) once registry publication is verified. Until then, use the review tarballs with the checksum-verifying `install-candidate.mjs`.
|
|
8
8
|
|
|
9
9
|
From your native agent workspace:
|
|
10
10
|
|
package/README.md
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
> Prerelease 0.3.0-rc.
|
|
1
|
+
> Prerelease 0.3.0-rc.5 targets fresh Eve 0.70 installations. Use the explicit candidate version or `next` tag after publication. Scoped `@tealbrick/*` `latest` remains 0.2.7 for existing Eve 0.66 installations; durable cross-version session migration is not validated.
|
|
2
2
|
|
|
3
3
|
# @tealbrick/kit
|
|
4
4
|
|
|
@@ -9,7 +9,7 @@ availability, not agent permissions. Nothing is mounted by installing the kit.
|
|
|
9
9
|
Install inside an existing Eve 0.70.0 project:
|
|
10
10
|
|
|
11
11
|
```sh
|
|
12
|
-
npm install --save-exact @tealbrick/kit@0.3.0-rc.
|
|
12
|
+
npm install --save-exact @tealbrick/kit@0.3.0-rc.5
|
|
13
13
|
npx --no-install tealbrick setup
|
|
14
14
|
```
|
|
15
15
|
|
|
@@ -110,6 +110,14 @@ approvals, subagents, workflows and schedules return 404/400. The native
|
|
|
110
110
|
harness keeps the conversation: Claude turns resume the same SDK session id,
|
|
111
111
|
and a restarted server only re-attaches transcripts that still exist.
|
|
112
112
|
|
|
113
|
+
While running, serve keeps the runtime connection synced with Portal even when
|
|
114
|
+
no turn is active: it fetches the signed runtime config and acknowledges it on
|
|
115
|
+
start and every 60–120 s (jittered; exponential backoff to 4 min on errors), so
|
|
116
|
+
Portal does not mark an idle runtime as expired after the 300 s config lease.
|
|
117
|
+
Changes are logged as `native.runtime.synced`; a revoked credential (HTTP 401)
|
|
118
|
+
stops the loop with one `native.runtime.sync.stopped` line. The per-turn
|
|
119
|
+
`tealbrick` MCP child still fetches a fresh authorization for every call.
|
|
120
|
+
|
|
113
121
|
### Security posture
|
|
114
122
|
|
|
115
123
|
- Binds `127.0.0.1` only. Reach it from the tailnet through a TLS-terminating
|
|
@@ -0,0 +1,49 @@
|
|
|
1
|
+
import { RuntimeConnector } from '@tealbrick/portal/runtime';
|
|
2
|
+
import type { JWTVerifyGetKey } from 'jose';
|
|
3
|
+
export type RuntimeCapabilities = Awaited<ReturnType<RuntimeConnector['capabilities']>>;
|
|
4
|
+
export type RuntimeSyncStatus = 'starting' | 'synced' | 'retrying' | 'inactive' | 'stopped';
|
|
5
|
+
export interface RuntimeSyncSnapshot {
|
|
6
|
+
status: RuntimeSyncStatus;
|
|
7
|
+
revision?: string;
|
|
8
|
+
expiresAt?: number;
|
|
9
|
+
lastSyncAt?: number;
|
|
10
|
+
failures: number;
|
|
11
|
+
error?: string;
|
|
12
|
+
capabilities?: RuntimeCapabilities;
|
|
13
|
+
}
|
|
14
|
+
export interface RuntimeSyncOptions {
|
|
15
|
+
fetch?: typeof fetch;
|
|
16
|
+
keys?: JWTVerifyGetKey;
|
|
17
|
+
log?: (event: Record<string, unknown>) => void;
|
|
18
|
+
/** Base delay between successful syncs. Portal's signed config lives 300s, so this must stay well below it. */
|
|
19
|
+
intervalMs?: number;
|
|
20
|
+
jitterMs?: number;
|
|
21
|
+
/** First retry delay after a failure; doubles per consecutive failure up to maxRetryMs. */
|
|
22
|
+
retryMs?: number;
|
|
23
|
+
maxRetryMs?: number;
|
|
24
|
+
random?: () => number;
|
|
25
|
+
now?: () => number;
|
|
26
|
+
}
|
|
27
|
+
/**
|
|
28
|
+
* Keeps this customer runtime's Portal configuration acknowledged while `native serve` is idle.
|
|
29
|
+
* The per-turn Teal Brick MCP child still fetches a fresh authorization for every call; this loop
|
|
30
|
+
* only fetches the signed config, acknowledges it (Portal derives runtime sync freshness from that)
|
|
31
|
+
* and holds the latest capability snapshot in memory. App credentials never leave the kit root.
|
|
32
|
+
*/
|
|
33
|
+
export declare function startRuntimeSync(root: string, options?: RuntimeSyncOptions): {
|
|
34
|
+
snapshot: () => RuntimeSyncSnapshot;
|
|
35
|
+
capabilities: () => {
|
|
36
|
+
revision: string;
|
|
37
|
+
expiresAt: number;
|
|
38
|
+
apps: {
|
|
39
|
+
registrationId: string;
|
|
40
|
+
appId: "knowledge";
|
|
41
|
+
actions: import("@tealbrick/portal/runtime").CrudAction[];
|
|
42
|
+
available: boolean;
|
|
43
|
+
operations: import("@tealbrick/portal/runtime").KnowledgeOperation[];
|
|
44
|
+
}[];
|
|
45
|
+
} | undefined;
|
|
46
|
+
syncNow: () => Promise<void>;
|
|
47
|
+
stop(): Promise<void>;
|
|
48
|
+
};
|
|
49
|
+
export type RuntimeSync = ReturnType<typeof startRuntimeSync>;
|
|
@@ -0,0 +1,101 @@
|
|
|
1
|
+
import { RuntimeConnector } from '@tealbrick/portal/runtime';
|
|
2
|
+
import { readKit } from './index.js';
|
|
3
|
+
import { runtimeSecret } from './secrets.js';
|
|
4
|
+
const defaults = { intervalMs: 90_000, jitterMs: 30_000, retryMs: 15_000, maxRetryMs: 240_000 };
|
|
5
|
+
const code = (v) => typeof v === 'string' && /^[a-z][a-z0-9_]{0,80}$/.test(v) ? v : undefined;
|
|
6
|
+
/**
|
|
7
|
+
* Keeps this customer runtime's Portal configuration acknowledged while `native serve` is idle.
|
|
8
|
+
* The per-turn Teal Brick MCP child still fetches a fresh authorization for every call; this loop
|
|
9
|
+
* only fetches the signed config, acknowledges it (Portal derives runtime sync freshness from that)
|
|
10
|
+
* and holds the latest capability snapshot in memory. App credentials never leave the kit root.
|
|
11
|
+
*/
|
|
12
|
+
export function startRuntimeSync(root, options = {}) {
|
|
13
|
+
const o = { ...defaults, ...options }, random = o.random ?? Math.random, now = o.now ?? Date.now;
|
|
14
|
+
const log = o.log ?? ((event) => process.stderr.write(JSON.stringify(event) + '\n'));
|
|
15
|
+
const base = o.fetch ?? fetch;
|
|
16
|
+
let state = { status: 'starting', failures: 0 };
|
|
17
|
+
let timer, stopped = false, running;
|
|
18
|
+
let connector, connectorKey;
|
|
19
|
+
let observed = {};
|
|
20
|
+
const observe = async (input, init) => {
|
|
21
|
+
const response = await base(input, init);
|
|
22
|
+
const path = new URL(input instanceof Request ? input.url : String(input)).pathname;
|
|
23
|
+
if (path === '/api/runtime/config' || path === '/api/runtime/ack') {
|
|
24
|
+
observed[path === '/api/runtime/config' ? 'config' : 'ack'] = response.status;
|
|
25
|
+
if (!response.ok)
|
|
26
|
+
observed.code = code(await response.clone().json().then((b) => b?.error ?? b?.code, () => undefined)) ?? observed.code;
|
|
27
|
+
}
|
|
28
|
+
return response;
|
|
29
|
+
};
|
|
30
|
+
const jitter = (ms, spread) => Math.max(1, Math.round(ms + (random() * 2 - 1) * spread));
|
|
31
|
+
function schedule(ms) { if (stopped)
|
|
32
|
+
return; clearTimeout(timer); timer = setTimeout(() => { void syncNow(); }, ms); timer.unref?.(); }
|
|
33
|
+
async function cycle() {
|
|
34
|
+
let kit;
|
|
35
|
+
try {
|
|
36
|
+
kit = await readKit(root);
|
|
37
|
+
}
|
|
38
|
+
catch {
|
|
39
|
+
kit = undefined;
|
|
40
|
+
}
|
|
41
|
+
if (!kit?.native?.active || !kit.runtime) {
|
|
42
|
+
if (state.status !== 'inactive')
|
|
43
|
+
log({ event: 'native.runtime.sync.inactive', message: 'Teal Brick runtime is not active in this kit root; Portal sync is paused.' });
|
|
44
|
+
connector = undefined;
|
|
45
|
+
connectorKey = undefined;
|
|
46
|
+
state = { status: 'inactive', failures: 0 };
|
|
47
|
+
schedule(jitter(o.intervalMs, o.jitterMs));
|
|
48
|
+
return;
|
|
49
|
+
}
|
|
50
|
+
const { credentialRef, ...runtime } = kit.runtime, key = JSON.stringify(kit.runtime);
|
|
51
|
+
if (!connector || key !== connectorKey) {
|
|
52
|
+
connector = new RuntimeConnector({ ...runtime, credential: () => runtimeSecret(credentialRef, root), resolveCredential: name => runtimeSecret(name, root), fetch: observe, ...(o.keys ? { keys: o.keys } : {}) });
|
|
53
|
+
connectorKey = key;
|
|
54
|
+
}
|
|
55
|
+
observed = {};
|
|
56
|
+
let capabilities;
|
|
57
|
+
try {
|
|
58
|
+
capabilities = await connector.capabilities(true);
|
|
59
|
+
}
|
|
60
|
+
catch (error) {
|
|
61
|
+
const reason = code(error?.message) ?? 'runtime_sync_failed';
|
|
62
|
+
if (observed.config === 401) {
|
|
63
|
+
const why = observed.code ?? 'runtime_credential_invalid';
|
|
64
|
+
stopped = true;
|
|
65
|
+
clearTimeout(timer);
|
|
66
|
+
state = { ...state, status: 'stopped', error: why };
|
|
67
|
+
log({ event: 'native.runtime.sync.stopped', reason: 'credential_revoked', code: why, message: `Portal rejected this runtime's credential (${why}); background runtime sync has stopped and Portal will show the runtime as expired. Re-enroll with \`tealbrick native setup\`, then restart native serve.` });
|
|
68
|
+
return;
|
|
69
|
+
}
|
|
70
|
+
const failures = state.failures + 1, delay = jitter(Math.min(o.maxRetryMs, o.retryMs * 2 ** (failures - 1)), Math.min(o.maxRetryMs, o.retryMs * 2 ** (failures - 1)) * 0.2);
|
|
71
|
+
state = { ...state, status: 'retrying', failures, error: observed.code ?? reason };
|
|
72
|
+
log({ event: 'native.runtime.sync.failed', code: state.error, ...(observed.config !== undefined ? { status: observed.config } : {}), failures, retryInMs: delay });
|
|
73
|
+
schedule(delay);
|
|
74
|
+
return;
|
|
75
|
+
}
|
|
76
|
+
const changed = state.status !== 'synced' || state.revision !== capabilities.revision;
|
|
77
|
+
const ackFailed = observed.ack !== undefined && !(observed.ack >= 200 && observed.ack < 300);
|
|
78
|
+
state = { status: ackFailed ? 'retrying' : 'synced', revision: capabilities.revision, expiresAt: capabilities.expiresAt, lastSyncAt: now(), failures: ackFailed ? state.failures + 1 : 0, capabilities, ...(ackFailed ? { error: observed.code ?? 'runtime_ack_failed' } : {}) };
|
|
79
|
+
if (ackFailed)
|
|
80
|
+
log({ event: 'native.runtime.ack.failed', code: state.error, status: observed.ack, failures: state.failures });
|
|
81
|
+
else if (changed)
|
|
82
|
+
log({ event: 'native.runtime.synced', revision: capabilities.revision.slice(0, 12), apps: capabilities.apps.length, available: capabilities.apps.filter(a => a.available).length, expiresAt: new Date(capabilities.expiresAt).toISOString() });
|
|
83
|
+
// Never let the acknowledged lease lapse between syncs; a failed ack retries on the short schedule.
|
|
84
|
+
const next = ackFailed ? jitter(o.retryMs, o.retryMs * 0.2) : jitter(o.intervalMs, o.jitterMs);
|
|
85
|
+
schedule(Math.max(1, Math.min(next, capabilities.expiresAt - now() - 30_000)));
|
|
86
|
+
}
|
|
87
|
+
async function syncNow() {
|
|
88
|
+
if (stopped)
|
|
89
|
+
return;
|
|
90
|
+
running ??= cycle().catch(error => { log({ event: 'native.runtime.sync.failed', code: code(error?.message) ?? 'runtime_sync_failed' }); schedule(o.maxRetryMs); }).finally(() => { running = undefined; });
|
|
91
|
+
return running;
|
|
92
|
+
}
|
|
93
|
+
void syncNow();
|
|
94
|
+
return {
|
|
95
|
+
snapshot: () => ({ ...state }),
|
|
96
|
+
capabilities: () => state.capabilities,
|
|
97
|
+
syncNow,
|
|
98
|
+
async stop() { stopped = true; clearTimeout(timer); await running; if (state.status !== 'stopped')
|
|
99
|
+
state = { ...state, status: 'stopped' }; },
|
|
100
|
+
};
|
|
101
|
+
}
|
package/dist/native-serve.d.ts
CHANGED
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import { type AuthResult } from '@tealbrick/portal';
|
|
2
2
|
import { type ClaudeSdk } from './native-claude.js';
|
|
3
|
+
import { type RuntimeSyncOptions } from './native-runtime-sync.js';
|
|
3
4
|
/** LangChain has no stable session/stream/cancel contract to drive from here yet. */
|
|
4
5
|
export declare function langchainHarness(): never;
|
|
5
6
|
export interface ServeDependencies {
|
|
@@ -7,11 +8,29 @@ export interface ServeDependencies {
|
|
|
7
8
|
claudeSdk?: ClaudeSdk;
|
|
8
9
|
log?: (event: Record<string, unknown>) => void;
|
|
9
10
|
env?: NodeJS.ProcessEnv;
|
|
11
|
+
/** Background Portal config/ack sync for this runtime; false disables it (tests only). */
|
|
12
|
+
runtimeSync?: RuntimeSyncOptions | false;
|
|
10
13
|
}
|
|
11
14
|
/** Build (but do not bind) the owner-authenticated chat endpoint for this kit root. */
|
|
12
15
|
export declare function createNativeServe(root: string, deps?: ServeDependencies): Promise<{
|
|
13
16
|
port: number;
|
|
14
17
|
harness: "claude" | "codex" | "langchain";
|
|
18
|
+
runtime: {
|
|
19
|
+
snapshot: () => import("./native-runtime-sync.js").RuntimeSyncSnapshot;
|
|
20
|
+
capabilities: () => {
|
|
21
|
+
revision: string;
|
|
22
|
+
expiresAt: number;
|
|
23
|
+
apps: {
|
|
24
|
+
registrationId: string;
|
|
25
|
+
appId: "knowledge";
|
|
26
|
+
actions: import("@tealbrick/portal/runtime").CrudAction[];
|
|
27
|
+
available: boolean;
|
|
28
|
+
operations: import("@tealbrick/portal/runtime").KnowledgeOperation[];
|
|
29
|
+
}[];
|
|
30
|
+
} | undefined;
|
|
31
|
+
syncNow: () => Promise<void>;
|
|
32
|
+
stop(): Promise<void>;
|
|
33
|
+
} | undefined;
|
|
15
34
|
close: () => Promise<void>;
|
|
16
35
|
server: import("http").Server;
|
|
17
36
|
sessions: Map<string, import("@tealbrick/portal/native-bridge").BridgeSession<any>>;
|
package/dist/native-serve.js
CHANGED
|
@@ -11,6 +11,7 @@ import { CodexAppServer, codexHarness } from '@tealbrick/portal/native-codex';
|
|
|
11
11
|
import { readKit } from './index.js';
|
|
12
12
|
import { nativeSdkConfig } from './native-setup.js';
|
|
13
13
|
import { claudeHarness, loadClaudeSdk } from './native-claude.js';
|
|
14
|
+
import { startRuntimeSync } from './native-runtime-sync.js';
|
|
14
15
|
import { publicHosts, readClaudeAttachment, readServeConfig, resolveClaudeProfile, servePaths } from './native-serve-config.js';
|
|
15
16
|
/** LangChain has no stable session/stream/cancel contract to drive from here yet. */
|
|
16
17
|
export function langchainHarness() { throw Error('kit_native_serve_langchain_unsupported'); }
|
|
@@ -49,7 +50,10 @@ export async function createNativeServe(root, deps = {}) {
|
|
|
49
50
|
adapter = langchainHarness();
|
|
50
51
|
const authenticate = deps.authenticate ?? createPortalVerifier({ issuer: kit.runtime.issuer, org: kit.runtime.org, agent: config.agent.name }).authenticate;
|
|
51
52
|
const bridge = await createNativeBridge({ name: config.agent.name, ownerId: config.agent.ownerId, root: paths.state, port: config.port, allowedHosts: publicHosts(config), authenticate, adapter, maxActiveTurns: config.maxActiveTurns ?? 2, log });
|
|
52
|
-
|
|
53
|
+
// Portal marks a runtime expired unless it re-acknowledges its signed config within the 300s lease;
|
|
54
|
+
// an idle serve process has no per-turn MCP child doing that, so it keeps the lease itself.
|
|
55
|
+
const runtime = deps.runtimeSync === false ? undefined : startRuntimeSync(root, { log, ...deps.runtimeSync });
|
|
56
|
+
return { ...bridge, port: config.port, harness: kit.native.harness, runtime, close: async () => { await runtime?.stop(); await bridge.close(); await codex?.close(); } };
|
|
53
57
|
}
|
|
54
58
|
export async function serveNative(root) {
|
|
55
59
|
const serve = await createNativeServe(root);
|
package/dist/secrets.d.ts
CHANGED
|
@@ -1,3 +1,4 @@
|
|
|
1
|
-
|
|
1
|
+
/** Reads from the environment, then the kit root's private credentials file (cwd unless `root` is given). */
|
|
2
|
+
export declare function runtimeSecret(name: string, root?: string): string;
|
|
2
3
|
/** Lazy: does not read credentials during discovery/build or for unused providers. */
|
|
3
4
|
export declare function secretEnvironment(): NodeJS.ProcessEnv;
|
package/dist/secrets.js
CHANGED
|
@@ -1,12 +1,14 @@
|
|
|
1
1
|
import { readFileSync, lstatSync } from 'node:fs';
|
|
2
|
+
import { join } from 'node:path';
|
|
2
3
|
const namePattern = /^TEALBRICK_[A-Z0-9_]+$/;
|
|
3
|
-
|
|
4
|
+
/** Reads from the environment, then the kit root's private credentials file (cwd unless `root` is given). */
|
|
5
|
+
export function runtimeSecret(name, root) {
|
|
4
6
|
if (!namePattern.test(name))
|
|
5
7
|
throw Error('kit_invalid_secret_reference');
|
|
6
8
|
let value = process.env[name];
|
|
7
9
|
if (!value) {
|
|
8
10
|
try {
|
|
9
|
-
const path = '.tealbrick/kit.credentials.json', stat = lstatSync(path);
|
|
11
|
+
const path = root === undefined ? '.tealbrick/kit.credentials.json' : join(root, '.tealbrick', 'kit.credentials.json'), stat = lstatSync(path);
|
|
10
12
|
if (!stat.isFile() || stat.isSymbolicLink() || (process.platform !== 'win32' && (stat.mode & 0o077)))
|
|
11
13
|
throw Error('kit_secret_permissions');
|
|
12
14
|
const data = JSON.parse(readFileSync(path, 'utf8'));
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@tealbrick/kit",
|
|
3
|
-
"version": "0.3.0-rc.
|
|
3
|
+
"version": "0.3.0-rc.5",
|
|
4
4
|
"type": "module",
|
|
5
5
|
"license": "MIT",
|
|
6
6
|
"description": "Complete Teal Brick package kit with explicit Eve capability activation",
|
|
@@ -43,11 +43,11 @@
|
|
|
43
43
|
"test": "node --test test/*.test.mjs"
|
|
44
44
|
},
|
|
45
45
|
"dependencies": {
|
|
46
|
-
"@tealbrick/portal": "0.3.0-rc.
|
|
47
|
-
"@tealbrick/avm": "0.3.0-rc.
|
|
48
|
-
"@tealbrick/voice": "0.3.0-rc.
|
|
49
|
-
"@tealbrick/vision": "0.3.0-rc.
|
|
50
|
-
"@tealbrick/deliver": "0.3.0-rc.
|
|
46
|
+
"@tealbrick/portal": "0.3.0-rc.5",
|
|
47
|
+
"@tealbrick/avm": "0.3.0-rc.5",
|
|
48
|
+
"@tealbrick/voice": "0.3.0-rc.5",
|
|
49
|
+
"@tealbrick/vision": "0.3.0-rc.5",
|
|
50
|
+
"@tealbrick/deliver": "0.3.0-rc.5",
|
|
51
51
|
"@inquirer/prompts": "8.7.2",
|
|
52
52
|
"zod": "^4.0.0"
|
|
53
53
|
},
|