@kazzle/app 0.1.821 → 0.1.823
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/client.d.ts +12 -5
- package/dist/client.js +27 -16
- package/dist/tools.js +2 -1
- package/package.json +1 -1
- package/templates/process-app/components/process/index.ts +10 -13
package/dist/client.d.ts
CHANGED
|
@@ -8,16 +8,23 @@ export declare function componentUrlEnvName(componentName: string): string;
|
|
|
8
8
|
* a wrong/empty address makes cross-component calls fail silently.
|
|
9
9
|
*/
|
|
10
10
|
export declare function componentUrl(componentName: string): string;
|
|
11
|
-
/**
|
|
12
|
-
export declare
|
|
11
|
+
/** Header Kazzle sets to carry the verified install identity into the app. */
|
|
12
|
+
export declare const KAZZLE_IDENTITY_HEADER = "x-kazzle-identity";
|
|
13
13
|
/**
|
|
14
|
-
*
|
|
14
|
+
* Extract the install identity token from an incoming tool request. Kazzle
|
|
15
|
+
* delivers it in `X-Kazzle-Identity`; the app's own `Authorization` header is
|
|
16
|
+
* left untouched for the app's own use.
|
|
17
|
+
*/
|
|
18
|
+
export declare function installIdentityToken(req: Request): string;
|
|
19
|
+
/**
|
|
20
|
+
* Client scoped to one install, built from the identity token on an incoming
|
|
15
21
|
* tool request. Use it to persist and read per-install runtime secrets.
|
|
16
22
|
*/
|
|
17
23
|
export declare class KazzleInstallClient {
|
|
18
|
-
private readonly
|
|
24
|
+
private readonly token;
|
|
19
25
|
private readonly apiUrl;
|
|
20
|
-
constructor(
|
|
26
|
+
constructor(reqOrIdentityToken: Request | string);
|
|
27
|
+
private authHeader;
|
|
21
28
|
/** Read a per-install secret by fixed name. Returns null when not set yet (404). */
|
|
22
29
|
getSecret(name: string): Promise<string | null>;
|
|
23
30
|
/** Write a per-install secret by fixed name. */
|
package/dist/client.js
CHANGED
|
@@ -4,10 +4,12 @@
|
|
|
4
4
|
// comments in the templates:
|
|
5
5
|
//
|
|
6
6
|
// 1. Per-install secrets. When the AI calls an app tool, Kazzle POSTs the
|
|
7
|
-
// request with
|
|
8
|
-
//
|
|
9
|
-
//
|
|
10
|
-
//
|
|
7
|
+
// request with the install identity in the `X-Kazzle-Identity` header
|
|
8
|
+
// (Kazzle owns that header; your app's own `Authorization` is never
|
|
9
|
+
// touched). The app reads or writes runtime secrets by calling Kazzle back
|
|
10
|
+
// with that identity as `Authorization: Bearer <identity>`; Kazzle scopes
|
|
11
|
+
// the secret to this install. Use a fixed secret name — never key by user
|
|
12
|
+
// yourself.
|
|
11
13
|
// 2. Sibling component URLs. A component reaches another component in the same
|
|
12
14
|
// app via the injected `KAZZLE_APP_COMPONENT_<NAME>_URL` env var. That URL
|
|
13
15
|
// is server-to-server only — never expose it to the browser.
|
|
@@ -42,29 +44,38 @@ export function componentUrl(componentName) {
|
|
|
42
44
|
}
|
|
43
45
|
return url;
|
|
44
46
|
}
|
|
45
|
-
/**
|
|
46
|
-
export
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
47
|
+
/** Header Kazzle sets to carry the verified install identity into the app. */
|
|
48
|
+
export const KAZZLE_IDENTITY_HEADER = 'x-kazzle-identity';
|
|
49
|
+
/**
|
|
50
|
+
* Extract the install identity token from an incoming tool request. Kazzle
|
|
51
|
+
* delivers it in `X-Kazzle-Identity`; the app's own `Authorization` header is
|
|
52
|
+
* left untouched for the app's own use.
|
|
53
|
+
*/
|
|
54
|
+
export function installIdentityToken(req) {
|
|
55
|
+
const token = req.headers.get(KAZZLE_IDENTITY_HEADER);
|
|
56
|
+
if (!token) {
|
|
57
|
+
throw new Error(`Missing ${KAZZLE_IDENTITY_HEADER} header — expected the install identity from a Kazzle tool call.`);
|
|
50
58
|
}
|
|
51
|
-
return
|
|
59
|
+
return token;
|
|
52
60
|
}
|
|
53
61
|
/**
|
|
54
|
-
* Client scoped to one install, built from the identity
|
|
62
|
+
* Client scoped to one install, built from the identity token on an incoming
|
|
55
63
|
* tool request. Use it to persist and read per-install runtime secrets.
|
|
56
64
|
*/
|
|
57
65
|
export class KazzleInstallClient {
|
|
58
|
-
|
|
66
|
+
token;
|
|
59
67
|
apiUrl;
|
|
60
|
-
constructor(
|
|
61
|
-
this.
|
|
68
|
+
constructor(reqOrIdentityToken) {
|
|
69
|
+
this.token = typeof reqOrIdentityToken === 'string' ? reqOrIdentityToken : installIdentityToken(reqOrIdentityToken);
|
|
62
70
|
this.apiUrl = kazzleApiUrl();
|
|
63
71
|
}
|
|
72
|
+
authHeader() {
|
|
73
|
+
return `Bearer ${this.token}`;
|
|
74
|
+
}
|
|
64
75
|
/** Read a per-install secret by fixed name. Returns null when not set yet (404). */
|
|
65
76
|
async getSecret(name) {
|
|
66
77
|
const res = await fetch(`${this.apiUrl}/apps/secrets/${encodeURIComponent(name)}`, {
|
|
67
|
-
headers: { authorization: this.
|
|
78
|
+
headers: { authorization: this.authHeader() },
|
|
68
79
|
});
|
|
69
80
|
if (res.status === 404)
|
|
70
81
|
return null;
|
|
@@ -77,7 +88,7 @@ export class KazzleInstallClient {
|
|
|
77
88
|
async setSecret(name, value) {
|
|
78
89
|
const res = await fetch(`${this.apiUrl}/apps/secrets/${encodeURIComponent(name)}`, {
|
|
79
90
|
method: 'PUT',
|
|
80
|
-
headers: { authorization: this.
|
|
91
|
+
headers: { authorization: this.authHeader(), 'content-type': 'application/json' },
|
|
81
92
|
body: JSON.stringify({ value }),
|
|
82
93
|
});
|
|
83
94
|
if (!res.ok)
|
package/dist/tools.js
CHANGED
|
@@ -18,7 +18,8 @@
|
|
|
18
18
|
// If you change this contract, update that compiler and its tests together.
|
|
19
19
|
//
|
|
20
20
|
// Tool handlers (POST /tools/...) receive:
|
|
21
|
-
// - `
|
|
21
|
+
// - `X-Kazzle-Identity: <install identity>` — scope secrets via KazzleInstallClient
|
|
22
|
+
// (your app's own `Authorization` header is never touched by Kazzle)
|
|
22
23
|
// - `Kazzle-Tool-Context: <json>` — thread vs Tools API (`toolContext(req)`)
|
|
23
24
|
// Handlers may return plain text, `{ content }`, or `{ type: "action_required", ... }`
|
|
24
25
|
// to pause the thread until the user acts (see documents/tool-required-action.md).
|
package/package.json
CHANGED
|
@@ -110,8 +110,9 @@ const server = Bun.serve({
|
|
|
110
110
|
//
|
|
111
111
|
// Each tool declared in skills/tools/tools.ts is backed by a route here.
|
|
112
112
|
// When the AI calls `save_bookmark`, Kazzle POSTs the tool input to this path
|
|
113
|
-
// with the app identity
|
|
114
|
-
//
|
|
113
|
+
// with the app identity in the `X-Kazzle-Identity` header (your app's own
|
|
114
|
+
// `Authorization` header is never touched). Return either plain text or JSON
|
|
115
|
+
// { content } — that becomes the tool result the AI sees.
|
|
115
116
|
//
|
|
116
117
|
// To add a tool: add it to tools.ts (name + Zod input + path), then add
|
|
117
118
|
// its matching `POST <path>` route below.
|
|
@@ -119,24 +120,20 @@ const server = Bun.serve({
|
|
|
119
120
|
const input = await req.json().catch(() => ({}));
|
|
120
121
|
const parsed = SaveBookmarkInput.safeParse(input);
|
|
121
122
|
if (!parsed.success) {
|
|
122
|
-
return Response.json({ content: parsed.error.issues.map(issue => issue.message).join('; ') }, { status: 400 });
|
|
123
|
+
return Response.json({ content: parsed.error.issues.map(issue => `${issue.path.join('.') || 'input'}: ${issue.message}`).join('; ') }, { status: 400 });
|
|
123
124
|
}
|
|
124
125
|
const toolInput = parsed.data;
|
|
125
126
|
// Do the real work here (write to your DB, call an API, etc.).
|
|
126
127
|
//
|
|
127
128
|
// Per-install runtime secrets (e.g. a connected user's OAuth token) are
|
|
128
|
-
// saved/read
|
|
129
|
-
//
|
|
129
|
+
// saved/read via KazzleInstallClient, which reads the `X-Kazzle-Identity`
|
|
130
|
+
// header from this request and scopes the secret to this install. Use a
|
|
130
131
|
// fixed name; never key by user yourself:
|
|
131
132
|
//
|
|
132
|
-
//
|
|
133
|
-
// const
|
|
134
|
-
// await
|
|
135
|
-
//
|
|
136
|
-
// body: JSON.stringify({ value: accessToken }),
|
|
137
|
-
// });
|
|
138
|
-
// const res = await fetch(`${api}/apps/secrets/gmail_token`, { headers: { authorization: auth } });
|
|
139
|
-
// const token = res.ok ? (await res.json()).value : null; // 404 → not connected yet
|
|
133
|
+
// import { KazzleInstallClient } from '@kazzle/app/client';
|
|
134
|
+
// const kazzle = new KazzleInstallClient(req);
|
|
135
|
+
// await kazzle.setSecret('gmail_token', accessToken);
|
|
136
|
+
// const token = await kazzle.getSecret('gmail_token'); // null → not connected yet
|
|
140
137
|
console.log(`[{{APP_NAME}}] save_bookmark:`, toolInput.title ?? '(untitled)', toolInput.url);
|
|
141
138
|
return Response.json({ content: `Saved bookmark: ${toolInput.title ?? toolInput.url}` });
|
|
142
139
|
}
|