@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 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
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@inferencesh/app",
3
- "version": "0.2.0",
3
+ "version": "0.3.0",
4
4
  "description": "App framework for building inference.sh apps — File handling, output metadata, storage utilities",
5
5
  "type": "module",
6
6
  "main": "dist/index.js",