@tealbrick/kit 0.3.0-rc.1 → 0.3.0-rc.4
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 +9 -3
- package/README.md +115 -2
- package/dist/cli.js +1 -1
- package/dist/index.d.ts +1 -0
- package/dist/index.js +1 -1
- package/dist/native-claude.d.ts +49 -0
- package/dist/native-claude.js +287 -0
- package/dist/native-enroll.d.ts +22 -0
- package/dist/native-enroll.js +85 -0
- package/dist/native-selection.js +19 -4
- package/dist/native-serve-config.d.ts +160 -0
- package/dist/native-serve-config.js +190 -0
- package/dist/native-serve.d.ts +50 -0
- package/dist/native-serve.js +124 -0
- package/dist/native-setup.js +38 -15
- package/dist/native.d.ts +2 -0
- package/dist/native.js +75 -3
- package/dist/onboarding.d.ts +5 -0
- package/dist/onboarding.js +1 -1
- package/package.json +6 -6
package/dist/native-setup.js
CHANGED
|
@@ -2,6 +2,7 @@ import { randomUUID } from 'node:crypto';
|
|
|
2
2
|
import { join, resolve } from 'node:path';
|
|
3
3
|
import { fileURLToPath } from 'node:url';
|
|
4
4
|
import { mkdir, lstat, readFile, writeFile, rename, unlink, realpath } from 'node:fs/promises';
|
|
5
|
+
import { PortalHttpError } from '@tealbrick/portal';
|
|
5
6
|
import { readKit } from './index.js';
|
|
6
7
|
import { selectNativeIdentity, harnessLabels } from './native-selection.js';
|
|
7
8
|
import { setupNative } from './native.js';
|
|
@@ -66,26 +67,48 @@ export async function guidedNativeSetup(root, flags, login, prompts) {
|
|
|
66
67
|
const stamp = { issuer: login.issuer, org: login.org, userId: login.userId, harness };
|
|
67
68
|
if (journal && JSON.stringify(journal.stamp) !== JSON.stringify(stamp))
|
|
68
69
|
throw Error('kit_native_setup_account_mismatch');
|
|
69
|
-
|
|
70
|
-
let
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
const
|
|
75
|
-
if (
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
70
|
+
let request = journal?.status !== 'rejected' ? journal?.request : undefined;
|
|
71
|
+
let resuming = !!request;
|
|
72
|
+
let selectionFlags = { ...flags };
|
|
73
|
+
if (request) {
|
|
74
|
+
// A prior POST may already have created the identity. Reconcile that exact key first.
|
|
75
|
+
const mismatched = (flags.agent && request.agent.id !== flags.agent) || (flags['create-agent'] && request.agent.mode === 'create' && request.agent.name !== flags['create-agent'].trim()) || (flags.workspace && request.workspace.id !== flags.workspace) || (flags['create-workspace'] && request.workspace.name !== flags['create-workspace'].trim());
|
|
76
|
+
if (mismatched)
|
|
77
|
+
throw Error('kit_native_setup_pending_intent_mismatch');
|
|
78
|
+
}
|
|
79
|
+
const saveJournal = async (status) => { const tmp = journalPath + '.' + randomUUID(); await writeFile(tmp, JSON.stringify({ version: 1, stamp, request, status }) + '\n', { flag: 'wx', mode: 0o600 }); await rename(tmp, journalPath); };
|
|
80
|
+
let prepared;
|
|
81
|
+
for (let attempt = 0;; attempt++) {
|
|
82
|
+
if (!request) {
|
|
83
|
+
const chosen = await selectNativeIdentity(root, selectionFlags, login, prompts);
|
|
84
|
+
const agent = 'id' in chosen.agent ? { mode: 'attach', id: chosen.agent.id, harness: harnessLabels[harness] } : { mode: 'create', name: chosen.agent.name, harness: harnessLabels[harness] };
|
|
85
|
+
if (old.runtime && (!('id' in chosen.workspace) || chosen.workspace.id !== old.runtime.workspaceId || !('id' in chosen.agent) || chosen.agent.id !== old.runtime.agentId))
|
|
79
86
|
throw Error('kit_runtime_existing_binding_mismatch');
|
|
80
|
-
request = {
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
87
|
+
request = { workspace: chosen.workspace, agent, idempotencyKey: randomUUID() };
|
|
88
|
+
await saveJournal('pending');
|
|
89
|
+
}
|
|
90
|
+
try {
|
|
91
|
+
prepared = await login.client.request('/api/runtime/setup', request);
|
|
92
|
+
break;
|
|
93
|
+
}
|
|
94
|
+
catch (error) {
|
|
95
|
+
// This exact Portal error occurs before workspace/agent mutation. Other failures
|
|
96
|
+
// retain the pending request, including timeouts, lost responses and HTTP 5xx.
|
|
97
|
+
if (!(error instanceof PortalHttpError) || error.status !== 409 || error.code !== 'agent_name_exists_select_existing' || attempt >= 3 || request.agent.mode !== 'create')
|
|
98
|
+
throw error;
|
|
99
|
+
await saveJournal('rejected');
|
|
100
|
+
resuming = false;
|
|
101
|
+
selectionFlags = { harness, ...(request.workspace.id ? { workspace: request.workspace.id } : { 'create-workspace': request.workspace.name }), 'create-agent': request.agent.name };
|
|
102
|
+
request = undefined;
|
|
84
103
|
}
|
|
85
104
|
}
|
|
86
|
-
const prepared = await login.client.request('/api/runtime/setup', request);
|
|
87
105
|
if (!prepared.workspace?.id || !prepared.agent?.id || !prepared.nodeId || prepared.agent.harness !== harnessLabels[harness])
|
|
88
106
|
throw Error('kit_native_setup_response_invalid');
|
|
107
|
+
if (resuming && ((flags['create-agent'] && prepared.agent.name !== flags['create-agent'].trim()) || (flags['agent-name'] && prepared.agent.name !== flags['agent-name']) || (flags['workspace-name'] && prepared.workspace.name !== flags['workspace-name'])))
|
|
108
|
+
throw Error('kit_native_setup_pending_intent_mismatch');
|
|
109
|
+
if (flags.node && flags.node !== prepared.nodeId)
|
|
110
|
+
throw Error('kit_owned_agent_card_required');
|
|
111
|
+
await saveJournal('prepared');
|
|
89
112
|
const authenticated = { ...login, workspaces: [...login.workspaces.filter(w => w.id !== prepared.workspace.id), prepared.workspace] };
|
|
90
113
|
const result = await setupNative(root, { harness, workspaceId: prepared.workspace.id, nodeId: prepared.nodeId, agentId: prepared.agent.id }, authenticated);
|
|
91
114
|
const tmp = configPath + '.' + randomUUID();
|
package/dist/native.d.ts
CHANGED
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
import { guidedNativeSetup, nativeSdkConfig } from './native-setup.js';
|
|
2
2
|
import { type PortalLogin } from './onboarding.js';
|
|
3
3
|
import { type KnowledgeOperation } from '@tealbrick/portal/runtime';
|
|
4
|
+
import { MarketplaceRuntimeConnector } from '@tealbrick/portal/marketplace-runtime';
|
|
4
5
|
declare const harnesses: {
|
|
5
6
|
readonly claude: "Claude Agent SDK";
|
|
6
7
|
readonly codex: "Codex";
|
|
@@ -22,6 +23,7 @@ export declare function setupNative(root: string, input: {
|
|
|
22
23
|
/** A fresh connector per invocation rechecks Portal and local activation. No authorization cache
|
|
23
24
|
* survives MCP reconnect, local deactivation or a request boundary. No session is created here. */
|
|
24
25
|
export declare function nativeConnector(): {
|
|
26
|
+
marketplace: () => Promise<MarketplaceRuntimeConnector | undefined>;
|
|
25
27
|
capabilities: () => Promise<{
|
|
26
28
|
revision: string;
|
|
27
29
|
expiresAt: number;
|
package/dist/native.js
CHANGED
|
@@ -4,6 +4,7 @@ import { applyKit, readKit } from './index.js';
|
|
|
4
4
|
import { loginPortal, portalOrigin } from './onboarding.js';
|
|
5
5
|
import { PortalHttpError } from '@tealbrick/portal';
|
|
6
6
|
import { RuntimeConnector } from '@tealbrick/portal/runtime';
|
|
7
|
+
import { MarketplaceRuntimeConnector } from '@tealbrick/portal/marketplace-runtime';
|
|
7
8
|
import { serveMcp } from '@tealbrick/portal/mcp';
|
|
8
9
|
import { runtimeSecret } from './secrets.js';
|
|
9
10
|
const harnesses = { claude: 'Claude Agent SDK', codex: 'Codex', langchain: 'LangChain' };
|
|
@@ -30,7 +31,7 @@ export async function setupNative(root, input, login) {
|
|
|
30
31
|
const enrolled = await login.client.request('/api/runtime/connections', { workspaceId: input.workspaceId, nodeId: input.nodeId, agentId: input.agentId });
|
|
31
32
|
if (!enrolled.connectionId || !enrolled.credential || !Number.isFinite(enrolled.expiresAt) || enrolled.expiresAt <= Date.now())
|
|
32
33
|
throw Error('kit_runtime_enrollment_failed');
|
|
33
|
-
await applyKit(root, { version: 1, native: { harness: input.harness, active: true }, runtime: { issuer: login.issuer, org: login.org, workspaceId: input.workspaceId, agentId: input.agentId, connectionId: enrolled.connectionId, credentialRef: 'TEALBRICK_RUNTIME_CREDENTIAL', bindings: old.runtime?.bindings ?? {} } }, { TEALBRICK_RUNTIME_CREDENTIAL: enrolled.credential });
|
|
34
|
+
await applyKit(root, { version: 1, native: { harness: input.harness, active: true, ...(old.native?.marketplace === undefined ? {} : { marketplace: old.native.marketplace }) }, runtime: { issuer: login.issuer, org: login.org, workspaceId: input.workspaceId, agentId: input.agentId, connectionId: enrolled.connectionId, credentialRef: 'TEALBRICK_RUNTIME_CREDENTIAL', bindings: old.runtime?.bindings ?? {} } }, { TEALBRICK_RUNTIME_CREDENTIAL: enrolled.credential });
|
|
34
35
|
return { connected: true, agentId: input.agentId, connectionId: enrolled.connectionId, harness: input.harness };
|
|
35
36
|
}
|
|
36
37
|
/** A fresh connector per invocation rechecks Portal and local activation. No authorization cache
|
|
@@ -43,9 +44,72 @@ export function nativeConnector() {
|
|
|
43
44
|
const { credentialRef, ...runtime } = config.runtime;
|
|
44
45
|
return new RuntimeConnector({ ...runtime, credential: () => runtimeSecret(credentialRef), resolveCredential: runtimeSecret });
|
|
45
46
|
}
|
|
46
|
-
return {
|
|
47
|
+
return { marketplace: async () => { const config = await readKit(process.cwd()); if (!config.native?.active || !config.native.marketplace || !config.runtime)
|
|
48
|
+
return undefined; const { credentialRef, ...identity } = config.runtime; return new MarketplaceRuntimeConnector({ ...identity, credential: () => runtimeSecret(credentialRef) }); }, capabilities: async () => (await current()).capabilities(true), call: async (registrationId, operation, input = {}, signal) => (await current()).call(registrationId, operation, input, signal) };
|
|
49
|
+
}
|
|
50
|
+
/** serve/enroll take boolean switches, so they are parsed separately from setup flags. */
|
|
51
|
+
function serveFlags(args, values, switches) {
|
|
52
|
+
const flags = {};
|
|
53
|
+
for (let i = 0; i < args.length; i++) {
|
|
54
|
+
const name = /^--([a-z-]+)$/.exec(args[i])?.[1];
|
|
55
|
+
if (!name || flags[name] !== undefined)
|
|
56
|
+
throw Error('kit_native_invalid_arguments');
|
|
57
|
+
if (switches.includes(name)) {
|
|
58
|
+
flags[name] = true;
|
|
59
|
+
continue;
|
|
60
|
+
}
|
|
61
|
+
if (!values.includes(name) || !args[i + 1] || args[i + 1].startsWith('--'))
|
|
62
|
+
throw Error('kit_native_invalid_arguments');
|
|
63
|
+
flags[name] = args[++i];
|
|
64
|
+
}
|
|
65
|
+
return flags;
|
|
47
66
|
}
|
|
48
67
|
export async function nativeCommand(args, root) {
|
|
68
|
+
if (args[0] === 'serve' || args[0] === 'enroll') {
|
|
69
|
+
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']);
|
|
70
|
+
if (typeof flags.root === 'string') {
|
|
71
|
+
root = resolve(flags.root);
|
|
72
|
+
process.chdir(root);
|
|
73
|
+
}
|
|
74
|
+
if (args[0] === 'serve') {
|
|
75
|
+
const { serveNative, installLaunchd, uninstallLaunchd } = await import('./native-serve.js');
|
|
76
|
+
if (flags['install-launchd'] && flags.uninstall)
|
|
77
|
+
throw Error('kit_native_invalid_arguments');
|
|
78
|
+
if (flags.uninstall) {
|
|
79
|
+
console.log(JSON.stringify(await uninstallLaunchd(root)));
|
|
80
|
+
return;
|
|
81
|
+
}
|
|
82
|
+
if (flags['install-launchd']) {
|
|
83
|
+
console.log(JSON.stringify(await installLaunchd(root, { load: !flags['no-load'] })));
|
|
84
|
+
return;
|
|
85
|
+
}
|
|
86
|
+
if (flags['no-load'])
|
|
87
|
+
throw Error('kit_native_invalid_arguments');
|
|
88
|
+
await serveNative(root);
|
|
89
|
+
return;
|
|
90
|
+
}
|
|
91
|
+
if (typeof flags.url !== 'string')
|
|
92
|
+
throw Error('kit_native_public_url_required');
|
|
93
|
+
const { enrollNative } = await import('./native-enroll.js');
|
|
94
|
+
const state = await readKit(root);
|
|
95
|
+
if (!state.native || !state.runtime)
|
|
96
|
+
throw Error('kit_native_setup_required');
|
|
97
|
+
const login = await loginPortal(portalOrigin(state.runtime.issuer), (url, code) => console.error(`Approve Tealbrick endpoint registration: ${url}\nCode: ${code}`));
|
|
98
|
+
try {
|
|
99
|
+
const result = await enrollNative(root, { url: flags.url, ...(typeof flags.port === 'string' ? { port: Number(flags.port) } : {}) }, login, { verifyEndpoint: !flags['skip-endpoint-check'] });
|
|
100
|
+
console.log(JSON.stringify(result));
|
|
101
|
+
if (!flags['skip-endpoint-check'] && !result.endpointVerified) {
|
|
102
|
+
console.error('Portal registration is saved. Start `tealbrick native serve` and the HTTPS proxy, then re-run enroll to verify the endpoint.');
|
|
103
|
+
process.exitCode = 2;
|
|
104
|
+
}
|
|
105
|
+
}
|
|
106
|
+
catch (error) {
|
|
107
|
+
if (error instanceof PortalHttpError && /^[a-z_]+$/.test(error.code ?? ''))
|
|
108
|
+
throw Error('kit_portal_' + error.code);
|
|
109
|
+
throw error;
|
|
110
|
+
}
|
|
111
|
+
return;
|
|
112
|
+
}
|
|
49
113
|
const command = args[0], flags = {};
|
|
50
114
|
for (let i = 1; i < args.length; i += 2) {
|
|
51
115
|
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)])
|
|
@@ -91,6 +155,14 @@ export async function nativeCommand(args, root) {
|
|
|
91
155
|
await serveMcp(nativeConnector());
|
|
92
156
|
return;
|
|
93
157
|
}
|
|
158
|
+
if (command === 'marketplace-enable' || command === 'marketplace-disable') {
|
|
159
|
+
const old = await readKit(root);
|
|
160
|
+
if (!old.native)
|
|
161
|
+
throw Error('kit_native_setup_required');
|
|
162
|
+
await applyKit(root, { ...old, native: { ...old.native, marketplace: command === 'marketplace-enable' } });
|
|
163
|
+
console.log(JSON.stringify({ marketplace: command === 'marketplace-enable' }));
|
|
164
|
+
return;
|
|
165
|
+
}
|
|
94
166
|
if (command === 'disable' || command === 'enable') {
|
|
95
167
|
const old = await readKit(root);
|
|
96
168
|
if (!old.native)
|
|
@@ -99,6 +171,6 @@ export async function nativeCommand(args, root) {
|
|
|
99
171
|
console.log(JSON.stringify({ active: command === 'enable' }));
|
|
100
172
|
return;
|
|
101
173
|
}
|
|
102
|
-
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 enable|disable');
|
|
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');
|
|
103
175
|
}
|
|
104
176
|
export { guidedNativeSetup, nativeSdkConfig };
|
package/dist/onboarding.d.ts
CHANGED
|
@@ -8,6 +8,11 @@ export interface PortalLogin {
|
|
|
8
8
|
id: string;
|
|
9
9
|
name: string;
|
|
10
10
|
}>;
|
|
11
|
+
/** Ephemeral user identity for endpoint verification. Never persisted. */
|
|
12
|
+
identity?: {
|
|
13
|
+
token: string;
|
|
14
|
+
bundle: string;
|
|
15
|
+
};
|
|
11
16
|
}
|
|
12
17
|
/** Reject URLs that would leak credentials or be silently truncated by registration. */
|
|
13
18
|
export declare function portalOrigin(value: string): string;
|
package/dist/onboarding.js
CHANGED
|
@@ -53,7 +53,7 @@ export async function loginPortal(issuer, show, options = {}) {
|
|
|
53
53
|
const directory = await authenticated.request('/api/me/workspaces');
|
|
54
54
|
if (!account.user?.id || !account.user.orgId || directory.accountId !== account.user.id || directory.organizationId !== account.user.orgId || !Array.isArray(directory.workspaces) || directory.workspaces.some(w => !w.id || !w.name))
|
|
55
55
|
throw Error('kit_portal_account_mismatch');
|
|
56
|
-
return { issuer, client: authenticated, org: account.user.orgId, userId: account.user.id, workspaces: directory.workspaces };
|
|
56
|
+
return { issuer, client: authenticated, org: account.user.orgId, userId: account.user.id, workspaces: directory.workspaces, identity: { token: session.token, bundle: session.bundle } };
|
|
57
57
|
}
|
|
58
58
|
/** Reuse Portal's owned registration + revision-checked canvas APIs, not a parallel card store. */
|
|
59
59
|
export async function registerPortalCard(login, input) {
|
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.4",
|
|
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.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",
|
|
51
51
|
"@inquirer/prompts": "8.7.2",
|
|
52
52
|
"zod": "^4.0.0"
|
|
53
53
|
},
|