@inferencesh/app 0.2.0 → 0.3.0
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/README.md +22 -0
- package/dist/index.d.ts +2 -0
- package/dist/index.js +2 -0
- package/dist/meta.d.ts +42 -0
- package/dist/meta.js +102 -0
- package/dist/types.d.ts +43 -1
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -23,6 +23,7 @@ npm install @inferencesh/app
|
|
|
23
23
|
| `download()` | download a url to a directory with caching |
|
|
24
24
|
| `textMeta`, `imageMeta`, `videoMeta`, `audioMeta`, `rawMeta` | output metadata factories for usage-based pricing |
|
|
25
25
|
| `createStreamSchema`, `pcm16`, `media`, `Live` | live fields for stream functions — values that flow over the socket while the task runs |
|
|
26
|
+
| `meta()` | per-function credentials and annotations (`<Name>Meta` exports) |
|
|
26
27
|
|
|
27
28
|
## file handling
|
|
28
29
|
|
|
@@ -297,6 +298,27 @@ the client closing the socket ends the `for await`, so the final yield goes afte
|
|
|
297
298
|
|
|
298
299
|
binary frames are perishable media, so the kernel keeps at most 256 of them queued for the app (about five seconds of 20 ms audio) and drops the oldest when the app reads slower than the client sends — latency stays bounded instead of growing for ever. text frames (commands and patches) are never dropped. `socket.dropped` counts the binary frames lost so far, and `socket.binaryBacklog = null` turns dropping off when every frame matters (a file sent in chunks).
|
|
299
300
|
|
|
301
|
+
## per-function credentials and annotations
|
|
302
|
+
|
|
303
|
+
export `<PascalName>Meta` next to a function's input and output schemas. `credentials` narrows what the function needs at run time (each entry must also be in inf.yml's `credentials:`; a function without it gets the app-wide list). `annotations` are hints about what it does.
|
|
304
|
+
|
|
305
|
+
```javascript
|
|
306
|
+
import { meta } from "@inferencesh/app";
|
|
307
|
+
|
|
308
|
+
export const CreateEventInput = z.object({ title: z.string() });
|
|
309
|
+
export const CreateEventOutput = z.object({ id: z.string() });
|
|
310
|
+
export const CreateEventMeta = meta({
|
|
311
|
+
credentials: [{ key: "google.calendar.events" }],
|
|
312
|
+
annotations: { readOnlyHint: false, destructiveHint: false },
|
|
313
|
+
});
|
|
314
|
+
|
|
315
|
+
export class App {
|
|
316
|
+
async createEvent(input) { /* ... */ }
|
|
317
|
+
}
|
|
318
|
+
```
|
|
319
|
+
|
|
320
|
+
`meta()` returns its argument unchanged after the same checks as sdk-py's `@function` (it throws on an empty `credentials` list, unknown keys, wrong value types, or a credential with neither `key` nor `provider`). it gives editors the `CredentialRequirement` and `ToolAnnotations` types (add `// @ts-check` to a `.js` file to get errors, not only completions).
|
|
321
|
+
|
|
300
322
|
## requirements
|
|
301
323
|
|
|
302
324
|
- node.js 18.0.0 or higher
|
package/dist/index.d.ts
CHANGED
|
@@ -4,6 +4,8 @@ export { createFileSchema, isFileSchema, FILE_SCHEMA_MARKER } from "./schema.js"
|
|
|
4
4
|
export type { Socket, Frame } from "./socket.js";
|
|
5
5
|
export { createStreamSchema, media, pcm16, Live, liveFields, binaryField, STREAM_SCHEMA_MARKER, CLEAR_KEY, ERROR_KEY, BINARY_SCHEMA_MARKER, } from "./stream.js";
|
|
6
6
|
export type { Update } from "./stream.js";
|
|
7
|
+
export { meta } from "./meta.js";
|
|
8
|
+
export type { FunctionMeta } from "./meta.js";
|
|
7
9
|
export { StorageDir, ensureDir } from "./storage.js";
|
|
8
10
|
export type { StorageDirValue } from "./storage.js";
|
|
9
11
|
export { download } from "./download.js";
|
package/dist/index.js
CHANGED
|
@@ -3,6 +3,8 @@ export { File } from "./file.js";
|
|
|
3
3
|
// Zod schema utilities
|
|
4
4
|
export { createFileSchema, isFileSchema, FILE_SCHEMA_MARKER } from "./schema.js";
|
|
5
5
|
export { createStreamSchema, media, pcm16, Live, liveFields, binaryField, STREAM_SCHEMA_MARKER, CLEAR_KEY, ERROR_KEY, BINARY_SCHEMA_MARKER, } from "./stream.js";
|
|
6
|
+
// Per-function metadata: credentials and annotations (<PascalName>Meta exports)
|
|
7
|
+
export { meta } from "./meta.js";
|
|
6
8
|
// Storage directories
|
|
7
9
|
export { StorageDir, ensureDir } from "./storage.js";
|
|
8
10
|
// Download utility
|
package/dist/meta.d.ts
ADDED
|
@@ -0,0 +1,42 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Per-function metadata: the credentials a function needs and hints about
|
|
3
|
+
* what it does. Node twin of sdk-py's `@function(credentials=..., annotations=...)`.
|
|
4
|
+
*
|
|
5
|
+
* Export it next to the function's Input and Output schemas, named
|
|
6
|
+
* `<PascalName>Meta`; the kernel's discovery reads it from there:
|
|
7
|
+
*
|
|
8
|
+
* ```js
|
|
9
|
+
* import { meta } from "@inferencesh/app";
|
|
10
|
+
*
|
|
11
|
+
* export const CreateEventInput = z.object({ ... });
|
|
12
|
+
* export const CreateEventOutput = z.object({ ... });
|
|
13
|
+
* export const CreateEventMeta = meta({
|
|
14
|
+
* credentials: [{ key: "google.calendar.events" }],
|
|
15
|
+
* annotations: { readOnlyHint: false, destructiveHint: false },
|
|
16
|
+
* });
|
|
17
|
+
*
|
|
18
|
+
* export class App { async createEvent(input) { ... } }
|
|
19
|
+
* ```
|
|
20
|
+
*/
|
|
21
|
+
import type { CredentialRequirement, ToolAnnotations } from "./types.js";
|
|
22
|
+
export interface FunctionMeta {
|
|
23
|
+
/**
|
|
24
|
+
* The credentials this function needs at run time. Each entry must be
|
|
25
|
+
* covered by the app-wide `credentials:` in inf.yml. A function without
|
|
26
|
+
* this gets the app-wide list.
|
|
27
|
+
*/
|
|
28
|
+
credentials?: CredentialRequirement[];
|
|
29
|
+
/** Hints about the function's behavior (read-only, destructive, ...). */
|
|
30
|
+
annotations?: ToolAnnotations;
|
|
31
|
+
}
|
|
32
|
+
/**
|
|
33
|
+
* Declares a function's metadata and returns `m` unchanged. Editors
|
|
34
|
+
* type-check the declaration, plain `.js` apps included (with `// @ts-check`).
|
|
35
|
+
*
|
|
36
|
+
* Throws on a malformed declaration, at import time, with the same checks as
|
|
37
|
+
* sdk-py's `@function`: an empty `credentials` list (omit it to use the
|
|
38
|
+
* app-wide list from inf.yml), unknown keys, wrong value types, or a
|
|
39
|
+
* credential with neither `key` nor `provider`. A null hint means
|
|
40
|
+
* "not declared"; the schema omits it.
|
|
41
|
+
*/
|
|
42
|
+
export declare function meta(m: FunctionMeta): FunctionMeta;
|
package/dist/meta.js
ADDED
|
@@ -0,0 +1,102 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Per-function metadata: the credentials a function needs and hints about
|
|
3
|
+
* what it does. Node twin of sdk-py's `@function(credentials=..., annotations=...)`.
|
|
4
|
+
*
|
|
5
|
+
* Export it next to the function's Input and Output schemas, named
|
|
6
|
+
* `<PascalName>Meta`; the kernel's discovery reads it from there:
|
|
7
|
+
*
|
|
8
|
+
* ```js
|
|
9
|
+
* import { meta } from "@inferencesh/app";
|
|
10
|
+
*
|
|
11
|
+
* export const CreateEventInput = z.object({ ... });
|
|
12
|
+
* export const CreateEventOutput = z.object({ ... });
|
|
13
|
+
* export const CreateEventMeta = meta({
|
|
14
|
+
* credentials: [{ key: "google.calendar.events" }],
|
|
15
|
+
* annotations: { readOnlyHint: false, destructiveHint: false },
|
|
16
|
+
* });
|
|
17
|
+
*
|
|
18
|
+
* export class App { async createEvent(input) { ... } }
|
|
19
|
+
* ```
|
|
20
|
+
*/
|
|
21
|
+
const CREDENTIAL_STR_KEYS = ["provider", "name", "website", "key", "description"];
|
|
22
|
+
const CREDENTIAL_LIST_KEYS = ["secrets", "scopes"];
|
|
23
|
+
const CREDENTIAL_KEYS = [...CREDENTIAL_STR_KEYS, "optional", ...CREDENTIAL_LIST_KEYS];
|
|
24
|
+
const ANNOTATION_HINT_KEYS = ["readOnlyHint", "destructiveHint", "idempotentHint", "openWorldHint"];
|
|
25
|
+
const ANNOTATION_KEYS = ["title", ...ANNOTATION_HINT_KEYS];
|
|
26
|
+
const isObject = (v) => v !== null && typeof v === "object" && !Array.isArray(v);
|
|
27
|
+
const typeName = (v) => (v === null ? "null" : Array.isArray(v) ? "array" : typeof v);
|
|
28
|
+
const unknownKeys = (obj, allowed) => Object.keys(obj).filter((k) => !allowed.includes(k)).sort();
|
|
29
|
+
function checkCredential(i, entry) {
|
|
30
|
+
const where = `meta() credentials[${i}]`;
|
|
31
|
+
if (!isObject(entry))
|
|
32
|
+
throw new TypeError(`${where}: expected an object, got ${typeName(entry)}`);
|
|
33
|
+
const unknown = unknownKeys(entry, CREDENTIAL_KEYS);
|
|
34
|
+
if (unknown.length > 0) {
|
|
35
|
+
throw new Error(`${where}: unknown keys ${JSON.stringify(unknown)}; allowed: ${JSON.stringify(CREDENTIAL_KEYS)}`);
|
|
36
|
+
}
|
|
37
|
+
for (const k of CREDENTIAL_STR_KEYS) {
|
|
38
|
+
if (entry[k] !== undefined && typeof entry[k] !== "string") {
|
|
39
|
+
throw new TypeError(`${where}.${k}: expected a string, got ${typeName(entry[k])}`);
|
|
40
|
+
}
|
|
41
|
+
}
|
|
42
|
+
if (entry.optional !== undefined && typeof entry.optional !== "boolean") {
|
|
43
|
+
throw new TypeError(`${where}.optional: expected a boolean, got ${typeName(entry.optional)}`);
|
|
44
|
+
}
|
|
45
|
+
for (const k of CREDENTIAL_LIST_KEYS) {
|
|
46
|
+
const v = entry[k];
|
|
47
|
+
if (v !== undefined && !(Array.isArray(v) && v.every((s) => typeof s === "string"))) {
|
|
48
|
+
throw new TypeError(`${where}.${k}: expected a list of strings`);
|
|
49
|
+
}
|
|
50
|
+
}
|
|
51
|
+
if (!entry.key && !entry.provider) {
|
|
52
|
+
throw new Error(`${where}: set 'key' (a capability like 'google.calendar.events') or 'provider'`);
|
|
53
|
+
}
|
|
54
|
+
}
|
|
55
|
+
function checkAnnotations(annotations) {
|
|
56
|
+
if (!isObject(annotations)) {
|
|
57
|
+
throw new TypeError(`meta() annotations: expected an object, got ${typeName(annotations)}`);
|
|
58
|
+
}
|
|
59
|
+
const unknown = unknownKeys(annotations, ANNOTATION_KEYS);
|
|
60
|
+
if (unknown.length > 0) {
|
|
61
|
+
throw new Error(`meta() annotations: unknown keys ${JSON.stringify(unknown)}; allowed: ${JSON.stringify(ANNOTATION_KEYS)}`);
|
|
62
|
+
}
|
|
63
|
+
if (annotations.title !== undefined && typeof annotations.title !== "string") {
|
|
64
|
+
throw new TypeError("meta() annotations.title: expected a string");
|
|
65
|
+
}
|
|
66
|
+
for (const k of ANNOTATION_HINT_KEYS) {
|
|
67
|
+
const v = annotations[k];
|
|
68
|
+
if (v !== undefined && v !== null && typeof v !== "boolean") {
|
|
69
|
+
throw new TypeError(`meta() annotations.${k}: expected a boolean, got ${typeName(v)}`);
|
|
70
|
+
}
|
|
71
|
+
}
|
|
72
|
+
}
|
|
73
|
+
/**
|
|
74
|
+
* Declares a function's metadata and returns `m` unchanged. Editors
|
|
75
|
+
* type-check the declaration, plain `.js` apps included (with `// @ts-check`).
|
|
76
|
+
*
|
|
77
|
+
* Throws on a malformed declaration, at import time, with the same checks as
|
|
78
|
+
* sdk-py's `@function`: an empty `credentials` list (omit it to use the
|
|
79
|
+
* app-wide list from inf.yml), unknown keys, wrong value types, or a
|
|
80
|
+
* credential with neither `key` nor `provider`. A null hint means
|
|
81
|
+
* "not declared"; the schema omits it.
|
|
82
|
+
*/
|
|
83
|
+
export function meta(m) {
|
|
84
|
+
if (!isObject(m))
|
|
85
|
+
throw new TypeError(`meta(): expected an object, got ${typeName(m)}`);
|
|
86
|
+
const unknown = unknownKeys(m, ["credentials", "annotations"]);
|
|
87
|
+
if (unknown.length > 0) {
|
|
88
|
+
throw new Error(`meta(): unknown keys ${JSON.stringify(unknown)}; allowed: ["credentials","annotations"]`);
|
|
89
|
+
}
|
|
90
|
+
if (m.credentials !== undefined) {
|
|
91
|
+
if (!Array.isArray(m.credentials)) {
|
|
92
|
+
throw new TypeError(`meta() credentials: expected a list of objects, got ${typeName(m.credentials)}`);
|
|
93
|
+
}
|
|
94
|
+
if (m.credentials.length === 0) {
|
|
95
|
+
throw new Error("meta() credentials: empty list; omit credentials to use the app-wide list from inf.yml");
|
|
96
|
+
}
|
|
97
|
+
m.credentials.forEach((c, i) => checkCredential(i, c));
|
|
98
|
+
}
|
|
99
|
+
if (m.annotations !== undefined)
|
|
100
|
+
checkAnnotations(m.annotations);
|
|
101
|
+
return m;
|
|
102
|
+
}
|
package/dist/types.d.ts
CHANGED
|
@@ -6,12 +6,54 @@ export interface RequestMetadata {
|
|
|
6
6
|
worker_id: string;
|
|
7
7
|
app_id: string;
|
|
8
8
|
app_version_id: string;
|
|
9
|
-
app_variant: string;
|
|
10
9
|
gpu_ids: string[];
|
|
11
10
|
task_id: string;
|
|
12
11
|
team_id: string;
|
|
13
12
|
user_id: string;
|
|
14
13
|
}
|
|
14
|
+
/**
|
|
15
|
+
* CredentialRequirement is an entry under credentials: in inf.yml: keys an
|
|
16
|
+
* app needs that belong to a provider. Provider names whose credential
|
|
17
|
+
* supplies them; Secrets lists the keys to inject from it (an API key
|
|
18
|
+
* credential), Scopes what to ask for (an OAuth one).
|
|
19
|
+
* credentials:
|
|
20
|
+
* - provider: acme
|
|
21
|
+
* name: Acme CRM
|
|
22
|
+
* website: acme.com
|
|
23
|
+
* secrets: [ACME_API_KEY]
|
|
24
|
+
* Key is a capability of a provider the platform defines ("x.tweet.read",
|
|
25
|
+
* "google.sheets"): an OAuth grant with the scopes it implies. An entry sets
|
|
26
|
+
* Provider, Key, or both; with both, Key decides how it is resolved.
|
|
27
|
+
*/
|
|
28
|
+
export interface CredentialRequirement {
|
|
29
|
+
provider?: string;
|
|
30
|
+
/**
|
|
31
|
+
* Name and Website describe a provider the platform does not list: the
|
|
32
|
+
* name its credential is shown under and the site its logo comes from.
|
|
33
|
+
*/
|
|
34
|
+
name?: string;
|
|
35
|
+
website?: string;
|
|
36
|
+
key?: string;
|
|
37
|
+
description?: string;
|
|
38
|
+
optional?: boolean;
|
|
39
|
+
secrets?: string[];
|
|
40
|
+
scopes?: string[];
|
|
41
|
+
}
|
|
42
|
+
/**
|
|
43
|
+
* ToolAnnotations describes tool behavior so clients can make trust and UX
|
|
44
|
+
* decisions (e.g., auto-approve read-only tools). All fields are optional hints.
|
|
45
|
+
*/
|
|
46
|
+
export interface ToolAnnotations {
|
|
47
|
+
/**
|
|
48
|
+
* Title is the human-readable name clients and the Claude directory show.
|
|
49
|
+
* It repeats MCPTool.Title because some clients read only this one.
|
|
50
|
+
*/
|
|
51
|
+
title?: string;
|
|
52
|
+
readOnlyHint?: boolean;
|
|
53
|
+
destructiveHint?: boolean;
|
|
54
|
+
idempotentHint?: boolean;
|
|
55
|
+
openWorldHint?: boolean;
|
|
56
|
+
}
|
|
15
57
|
export interface AppSDKTypes {
|
|
16
58
|
}
|
|
17
59
|
/**
|
package/package.json
CHANGED