@tealbrick/kit 0.3.0-rc.4 → 0.3.0-rc.6
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 +15 -2
- package/dist/native-app-capabilities.d.ts +29 -0
- package/dist/native-app-capabilities.js +36 -0
- package/dist/native-runtime-sync.d.ts +49 -0
- package/dist/native-runtime-sync.js +101 -0
- package/dist/native-serve.d.ts +22 -0
- package/dist/native-serve.js +9 -2
- package/dist/native.d.ts +1 -0
- package/dist/native.js +8 -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.6 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.6` 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.6
|
|
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.6** 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. RC6 adds the owner-only `/tealbrick/v1/capabilities` snapshot (Portal-granted apps and Marketplace consents) used by TBD Chat → Plugins, and `tealbrick native` without a subcommand prints usage.
|
|
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.6` (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.6 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.6
|
|
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
|
|
@@ -131,6 +139,11 @@ and a restarted server only re-attaches transcripts that still exist.
|
|
|
131
139
|
`Agent` delegates only to the configured `experts`, in the foreground with
|
|
132
140
|
the session model; each expert runs with its own `tools` (workspace reads,
|
|
133
141
|
`WebSearch`, `WebFetch`) and never receives Teal Brick tools.
|
|
142
|
+
- `GET /tealbrick/v1/capabilities` (same owner auth) reports what Portal
|
|
143
|
+
currently grants this agent: Knowledge apps from the background runtime sync
|
|
144
|
+
and, when `tealbrick native marketplace-enable` is set, Marketplace consents
|
|
145
|
+
(plugin and action names only; no credentials, leases or account
|
|
146
|
+
references). TBD uses it for Chat → Plugins.
|
|
134
147
|
- Elevation only through Desktop's session-access control (owner, explicit
|
|
135
148
|
confirmation, optimistic revision, no turn in flight). **Native defaults**
|
|
136
149
|
snapshots `nativeTools` (still workspace-contained); **Full** uses the
|
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
import type { RuntimeSync } from './native-runtime-sync.js';
|
|
2
|
+
export interface AppCapabilitiesOptions {
|
|
3
|
+
fetch?: typeof fetch;
|
|
4
|
+
now?: () => number;
|
|
5
|
+
marketplaceTtlMs?: number;
|
|
6
|
+
}
|
|
7
|
+
/**
|
|
8
|
+
* Owner-facing snapshot of what Portal currently grants this agent: Knowledge apps from the background
|
|
9
|
+
* runtime sync, and Marketplace consents when Marketplace is activated in this kit root. Identifiers,
|
|
10
|
+
* actions and plugin/action names only; never credentials, leases or provider account references.
|
|
11
|
+
*/
|
|
12
|
+
export declare function appCapabilitiesProvider(root: string, runtime: () => RuntimeSync | undefined, options?: AppCapabilitiesOptions): () => Promise<{
|
|
13
|
+
schema: number;
|
|
14
|
+
sync: {
|
|
15
|
+
error?: string | undefined;
|
|
16
|
+
lastSyncAt?: number | undefined;
|
|
17
|
+
expiresAt?: number | undefined;
|
|
18
|
+
revision?: string | undefined;
|
|
19
|
+
status: string;
|
|
20
|
+
};
|
|
21
|
+
apps: {
|
|
22
|
+
registrationId: string;
|
|
23
|
+
appId: "knowledge";
|
|
24
|
+
actions: import("@tealbrick/portal/runtime").CrudAction[];
|
|
25
|
+
available: boolean;
|
|
26
|
+
operations: import("@tealbrick/portal/runtime").KnowledgeOperation[];
|
|
27
|
+
}[];
|
|
28
|
+
marketplace: Record<string, unknown>;
|
|
29
|
+
}>;
|
|
@@ -0,0 +1,36 @@
|
|
|
1
|
+
import { MarketplaceRuntimeConnector } from '@tealbrick/portal/marketplace-runtime';
|
|
2
|
+
import { readKit } from './index.js';
|
|
3
|
+
import { runtimeSecret } from './secrets.js';
|
|
4
|
+
const code = (v) => typeof v === 'string' && /^[a-z][a-z0-9_]{0,80}$/.test(v) ? v : undefined;
|
|
5
|
+
/**
|
|
6
|
+
* Owner-facing snapshot of what Portal currently grants this agent: Knowledge apps from the background
|
|
7
|
+
* runtime sync, and Marketplace consents when Marketplace is activated in this kit root. Identifiers,
|
|
8
|
+
* actions and plugin/action names only; never credentials, leases or provider account references.
|
|
9
|
+
*/
|
|
10
|
+
export function appCapabilitiesProvider(root, runtime, options = {}) {
|
|
11
|
+
const now = options.now ?? Date.now, ttl = options.marketplaceTtlMs ?? 30_000;
|
|
12
|
+
let cached;
|
|
13
|
+
async function marketplace() {
|
|
14
|
+
const kit = await readKit(root);
|
|
15
|
+
if (!kit.native?.active || !kit.native.marketplace || !kit.runtime)
|
|
16
|
+
return { enabled: false, status: 'inactive', grants: [] };
|
|
17
|
+
const { credentialRef, ...identity } = kit.runtime, key = JSON.stringify(kit.runtime);
|
|
18
|
+
if (cached && cached.key === key && now() - cached.at < ttl)
|
|
19
|
+
return cached.value;
|
|
20
|
+
let value;
|
|
21
|
+
try {
|
|
22
|
+
const consents = await new MarketplaceRuntimeConnector({ ...identity, credential: () => runtimeSecret(credentialRef, root), ...(options.fetch ? { fetch: options.fetch } : {}) }).discover();
|
|
23
|
+
value = { enabled: true, status: 'ok', grants: consents.map(c => ({ registrationId: `marketplace:${c.consentId}`, consentId: c.consentId, consentRevision: c.consentRevision, deploymentId: c.deploymentId, state: c.state, plugin: c.selection.pluginId, action: c.selection.actionKey, resourceKind: c.selection.resourceKind, operations: ['marketplace_execute'] })) };
|
|
24
|
+
}
|
|
25
|
+
catch (e) {
|
|
26
|
+
value = { enabled: true, status: 'unavailable', error: code(e?.message) ?? 'runtime_marketplace_unavailable', grants: [] };
|
|
27
|
+
}
|
|
28
|
+
cached = { at: now(), key, value };
|
|
29
|
+
return value;
|
|
30
|
+
}
|
|
31
|
+
return async () => {
|
|
32
|
+
const snap = runtime()?.snapshot();
|
|
33
|
+
const apps = (snap?.capabilities?.apps ?? []).map(a => ({ registrationId: a.registrationId, appId: a.appId, actions: [...a.actions], available: a.available, operations: [...a.operations] }));
|
|
34
|
+
return { schema: 1, sync: { status: snap?.status ?? 'disabled', ...(snap?.revision ? { revision: snap.revision } : {}), ...(snap?.expiresAt ? { expiresAt: snap.expiresAt } : {}), ...(snap?.lastSyncAt ? { lastSyncAt: snap.lastSyncAt } : {}), ...(snap?.error ? { error: snap.error } : {}) }, apps, marketplace: await marketplace() };
|
|
35
|
+
};
|
|
36
|
+
}
|
|
@@ -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,7 @@
|
|
|
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';
|
|
4
|
+
import { type AppCapabilitiesOptions } from './native-app-capabilities.js';
|
|
3
5
|
/** LangChain has no stable session/stream/cancel contract to drive from here yet. */
|
|
4
6
|
export declare function langchainHarness(): never;
|
|
5
7
|
export interface ServeDependencies {
|
|
@@ -7,11 +9,31 @@ export interface ServeDependencies {
|
|
|
7
9
|
claudeSdk?: ClaudeSdk;
|
|
8
10
|
log?: (event: Record<string, unknown>) => void;
|
|
9
11
|
env?: NodeJS.ProcessEnv;
|
|
12
|
+
/** Background Portal config/ack sync for this runtime; false disables it (tests only). */
|
|
13
|
+
runtimeSync?: RuntimeSyncOptions | false;
|
|
14
|
+
/** Test seams for the owner-only /tealbrick/v1/capabilities snapshot. */
|
|
15
|
+
appCapabilities?: AppCapabilitiesOptions;
|
|
10
16
|
}
|
|
11
17
|
/** Build (but do not bind) the owner-authenticated chat endpoint for this kit root. */
|
|
12
18
|
export declare function createNativeServe(root: string, deps?: ServeDependencies): Promise<{
|
|
13
19
|
port: number;
|
|
14
20
|
harness: "claude" | "codex" | "langchain";
|
|
21
|
+
runtime: {
|
|
22
|
+
snapshot: () => import("./native-runtime-sync.js").RuntimeSyncSnapshot;
|
|
23
|
+
capabilities: () => {
|
|
24
|
+
revision: string;
|
|
25
|
+
expiresAt: number;
|
|
26
|
+
apps: {
|
|
27
|
+
registrationId: string;
|
|
28
|
+
appId: "knowledge";
|
|
29
|
+
actions: import("@tealbrick/portal/runtime").CrudAction[];
|
|
30
|
+
available: boolean;
|
|
31
|
+
operations: import("@tealbrick/portal/runtime").KnowledgeOperation[];
|
|
32
|
+
}[];
|
|
33
|
+
} | undefined;
|
|
34
|
+
syncNow: () => Promise<void>;
|
|
35
|
+
stop(): Promise<void>;
|
|
36
|
+
} | undefined;
|
|
15
37
|
close: () => Promise<void>;
|
|
16
38
|
server: import("http").Server;
|
|
17
39
|
sessions: Map<string, import("@tealbrick/portal/native-bridge").BridgeSession<any>>;
|
package/dist/native-serve.js
CHANGED
|
@@ -11,6 +11,8 @@ 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';
|
|
15
|
+
import { appCapabilitiesProvider } from './native-app-capabilities.js';
|
|
14
16
|
import { publicHosts, readClaudeAttachment, readServeConfig, resolveClaudeProfile, servePaths } from './native-serve-config.js';
|
|
15
17
|
/** LangChain has no stable session/stream/cancel contract to drive from here yet. */
|
|
16
18
|
export function langchainHarness() { throw Error('kit_native_serve_langchain_unsupported'); }
|
|
@@ -48,8 +50,13 @@ export async function createNativeServe(root, deps = {}) {
|
|
|
48
50
|
else
|
|
49
51
|
adapter = langchainHarness();
|
|
50
52
|
const authenticate = deps.authenticate ?? createPortalVerifier({ issuer: kit.runtime.issuer, org: kit.runtime.org, agent: config.agent.name }).authenticate;
|
|
51
|
-
|
|
52
|
-
|
|
53
|
+
let runtime;
|
|
54
|
+
const appCapabilities = appCapabilitiesProvider(root, () => runtime, deps.appCapabilities);
|
|
55
|
+
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, appCapabilities });
|
|
56
|
+
// Portal marks a runtime expired unless it re-acknowledges its signed config within the 300s lease;
|
|
57
|
+
// an idle serve process has no per-turn MCP child doing that, so it keeps the lease itself.
|
|
58
|
+
runtime = deps.runtimeSync === false ? undefined : startRuntimeSync(root, { log, ...deps.runtimeSync });
|
|
59
|
+
return { ...bridge, port: config.port, harness: kit.native.harness, runtime, close: async () => { await runtime?.stop(); await bridge.close(); await codex?.close(); } };
|
|
53
60
|
}
|
|
54
61
|
export async function serveNative(root) {
|
|
55
62
|
const serve = await createNativeServe(root);
|
package/dist/native.d.ts
CHANGED
|
@@ -37,5 +37,6 @@ export declare function nativeConnector(): {
|
|
|
37
37
|
}>;
|
|
38
38
|
call: (registrationId: string, operation: KnowledgeOperation, input?: Record<string, unknown>, signal?: AbortSignal) => Promise<any>;
|
|
39
39
|
};
|
|
40
|
+
export declare const nativeUsage = "Usage: tealbrick native setup --harness claude|codex|langchain [--workspace-name NAME | --create-workspace NAME] [--agent-name NAME | --create-agent NAME] [--issuer HTTPS_ORIGIN] | native mcp [--root DIRECTORY] | native serve [--root DIRECTORY] [--install-launchd [--no-load] | --uninstall] | native enroll --url HTTPS_ORIGIN [--port PORT] [--root DIRECTORY] [--skip-endpoint-check] | native enable|disable | native marketplace-enable|marketplace-disable";
|
|
40
41
|
export declare function nativeCommand(args: string[], root: string): Promise<void>;
|
|
41
42
|
export { guidedNativeSetup, nativeSdkConfig };
|
package/dist/native.js
CHANGED
|
@@ -64,6 +64,7 @@ function serveFlags(args, values, switches) {
|
|
|
64
64
|
}
|
|
65
65
|
return flags;
|
|
66
66
|
}
|
|
67
|
+
export const nativeUsage = 'Usage: tealbrick native setup --harness claude|codex|langchain [--workspace-name NAME | --create-workspace NAME] [--agent-name NAME | --create-agent NAME] [--issuer HTTPS_ORIGIN] | native mcp [--root DIRECTORY] | native serve [--root DIRECTORY] [--install-launchd [--no-load] | --uninstall] | native enroll --url HTTPS_ORIGIN [--port PORT] [--root DIRECTORY] [--skip-endpoint-check] | native enable|disable | native marketplace-enable|marketplace-disable';
|
|
67
68
|
export async function nativeCommand(args, root) {
|
|
68
69
|
if (args[0] === 'serve' || args[0] === 'enroll') {
|
|
69
70
|
const flags = args[0] === 'serve' ? serveFlags(args.slice(1), ['root'], ['install-launchd', 'uninstall', 'no-load']) : serveFlags(args.slice(1), ['root', 'url', 'port'], ['skip-endpoint-check']);
|
|
@@ -111,6 +112,11 @@ export async function nativeCommand(args, root) {
|
|
|
111
112
|
return;
|
|
112
113
|
}
|
|
113
114
|
const command = args[0], flags = {};
|
|
115
|
+
// A bare `tealbrick native` or help request prints usage; it is not a failed setup.
|
|
116
|
+
if (!command || command === 'help' || command === '--help' || command === '-h') {
|
|
117
|
+
console.log(nativeUsage);
|
|
118
|
+
return;
|
|
119
|
+
}
|
|
114
120
|
for (let i = 1; i < args.length; i += 2) {
|
|
115
121
|
if (!/^--(root|harness|workspace|workspace-name|create-workspace|node|agent|agent-name|create-agent|delegation|issuer)$/.test(args[i]) || !args[i + 1] || flags[args[i].slice(2)])
|
|
116
122
|
throw Error('kit_native_invalid_arguments');
|
|
@@ -171,6 +177,7 @@ export async function nativeCommand(args, root) {
|
|
|
171
177
|
console.log(JSON.stringify({ active: command === 'enable' }));
|
|
172
178
|
return;
|
|
173
179
|
}
|
|
174
|
-
|
|
180
|
+
console.error(nativeUsage);
|
|
181
|
+
process.exitCode = 2;
|
|
175
182
|
}
|
|
176
183
|
export { guidedNativeSetup, nativeSdkConfig };
|
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.6",
|
|
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.6",
|
|
47
|
+
"@tealbrick/avm": "0.3.0-rc.6",
|
|
48
|
+
"@tealbrick/voice": "0.3.0-rc.6",
|
|
49
|
+
"@tealbrick/vision": "0.3.0-rc.6",
|
|
50
|
+
"@tealbrick/deliver": "0.3.0-rc.6",
|
|
51
51
|
"@inquirer/prompts": "8.7.2",
|
|
52
52
|
"zod": "^4.0.0"
|
|
53
53
|
},
|