@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 CHANGED
@@ -1,4 +1,4 @@
1
- > Prerelease 0.3.0-rc.4 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.
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.4` inside that project.
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.4
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.4** 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 adds `tealbrick native serve` and `native enroll` (see README.md).
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.4` (or `tealbrick@next`) once registry publication is verified. Until then, use the review tarballs with the checksum-verifying `install-candidate.mjs`.
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.4 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.
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.4
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
+ }
@@ -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>>;
@@ -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
- 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
- return { ...bridge, port: config.port, harness: kit.native.harness, close: async () => { await bridge.close(); await codex?.close(); } };
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
- throw Error('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');
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
- export declare function runtimeSecret(name: string): string;
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
- export function runtimeSecret(name) {
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.4",
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.4",
47
- "@tealbrick/avm": "0.3.0-rc.4",
48
- "@tealbrick/voice": "0.3.0-rc.4",
49
- "@tealbrick/vision": "0.3.0-rc.4",
50
- "@tealbrick/deliver": "0.3.0-rc.4",
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
  },