@granular-software/sdk 0.4.5 → 0.4.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/README.md +16 -7
- package/dist/cli/index.js +33 -5
- package/dist/index.d.mts +9 -1
- package/dist/index.d.ts +9 -1
- package/dist/index.js +33 -2
- package/dist/index.js.map +1 -1
- package/dist/index.mjs +33 -2
- package/dist/index.mjs.map +1 -1
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -8,7 +8,7 @@ The official TypeScript SDK for Granular.
|
|
|
8
8
|
|
|
9
9
|
- 🏗️ **Domain Ontology** — Define classes, properties, and relationships as a typed graph
|
|
10
10
|
- 🔧 **Class-Based Tools** — Attach instance methods, static methods, and global tools with typed I/O
|
|
11
|
-
- 🤖 **Domain Synthesis** — Auto-generated TypeScript classes with `
|
|
11
|
+
- 🤖 **Domain Synthesis** — Auto-generated TypeScript classes with `get({ path })`, `list()`, relationship accessors, and typed methods
|
|
12
12
|
- 🔒 **Secure Execution** — Run AI-generated code in isolated sandboxes
|
|
13
13
|
- 👥 **User Permissions** — Control what each user can access
|
|
14
14
|
- ⚡ **Real-time** — WebSocket-based streaming and events
|
|
@@ -147,8 +147,10 @@ await granular.registerEffects(env.sandboxId, [
|
|
|
147
147
|
const job = await env.submitJob(`
|
|
148
148
|
import { Customer } from './sandbox-tools';
|
|
149
149
|
|
|
150
|
-
//
|
|
151
|
-
const
|
|
150
|
+
// list() discovers instances; get({ path }) hydrates a specific graph object
|
|
151
|
+
const customers = await Customer.list();
|
|
152
|
+
const acme = customers.find((customer) => customer.name === 'Acme Corp');
|
|
153
|
+
if (!acme) throw new Error('Customer not found');
|
|
152
154
|
console.log(acme.name); // "Acme Corp"
|
|
153
155
|
console.log(acme.email); // "billing@acme.com"
|
|
154
156
|
|
|
@@ -340,8 +342,11 @@ export declare class Author {
|
|
|
340
342
|
|
|
341
343
|
constructor(id: string, fields?: Record<string, any>);
|
|
342
344
|
|
|
343
|
-
/**
|
|
344
|
-
static
|
|
345
|
+
/** Get a cached Author by graph path, hydrating from the graph when needed */
|
|
346
|
+
static get(query: { path: string; refresh?: boolean }): Promise<Author | null>;
|
|
347
|
+
|
|
348
|
+
/** List known Author instances */
|
|
349
|
+
static list(query?: { limit?: number; saveAs?: string; refresh?: boolean }): Promise<Author[]>;
|
|
345
350
|
|
|
346
351
|
/** Get biography of an author */
|
|
347
352
|
get_bio(input?: { detailed?: boolean }): Promise<{ bio: string; source?: string }>;
|
|
@@ -359,7 +364,9 @@ export declare class Book {
|
|
|
359
364
|
readonly isbn: string;
|
|
360
365
|
readonly published_year: number;
|
|
361
366
|
|
|
362
|
-
static
|
|
367
|
+
static get(query: { path: string; refresh?: boolean }): Promise<Book | null>;
|
|
368
|
+
|
|
369
|
+
static list(query?: { limit?: number; saveAs?: string; refresh?: boolean }): Promise<Book[]>;
|
|
363
370
|
|
|
364
371
|
/** Navigate to author (many_to_one) */
|
|
365
372
|
get_author(): Promise<Author | null>;
|
|
@@ -374,7 +381,9 @@ The LLM or user writes code against these typed classes:
|
|
|
374
381
|
```typescript
|
|
375
382
|
import { Author, Book, global_search } from './sandbox-tools';
|
|
376
383
|
|
|
377
|
-
const
|
|
384
|
+
const authors = await Author.list();
|
|
385
|
+
const tolkien = authors.find((author) => author.name === 'J.R.R. Tolkien');
|
|
386
|
+
if (!tolkien) throw new Error('Author not found');
|
|
378
387
|
console.log(tolkien.name); // "J.R.R. Tolkien"
|
|
379
388
|
|
|
380
389
|
const bio = await tolkien.get_bio({ detailed: true });
|
package/dist/cli/index.js
CHANGED
|
@@ -7771,7 +7771,31 @@ async function main() {
|
|
|
7771
7771
|
await granular.registerEffects(SANDBOX_ID, ${effectsArray});
|
|
7772
7772
|
|
|
7773
7773
|
log('Effects registered. Process kept alive for simulator. Press Ctrl+C to exit.');
|
|
7774
|
-
|
|
7774
|
+
|
|
7775
|
+
const keepAliveTimer = setInterval(() => {
|
|
7776
|
+
// Keep an active event-loop handle so Bun/Node does not exit immediately.
|
|
7777
|
+
}, 60_000);
|
|
7778
|
+
|
|
7779
|
+
await new Promise<void>((resolve) => {
|
|
7780
|
+
let shuttingDown = false;
|
|
7781
|
+
|
|
7782
|
+
const shutdown = async (signal: string) => {
|
|
7783
|
+
if (shuttingDown) return;
|
|
7784
|
+
shuttingDown = true;
|
|
7785
|
+
clearInterval(keepAliveTimer);
|
|
7786
|
+
log(\`Received \${signal}. Shutting down effects host...\`);
|
|
7787
|
+
try {
|
|
7788
|
+
await granular.disconnectEffects(SANDBOX_ID);
|
|
7789
|
+
} catch (error) {
|
|
7790
|
+
console.warn('[Effects] Failed to disconnect live effects cleanly:', error);
|
|
7791
|
+
}
|
|
7792
|
+
resolve();
|
|
7793
|
+
process.exit(0);
|
|
7794
|
+
};
|
|
7795
|
+
|
|
7796
|
+
process.once('SIGINT', () => { void shutdown('SIGINT'); });
|
|
7797
|
+
process.once('SIGTERM', () => { void shutdown('SIGTERM'); });
|
|
7798
|
+
});
|
|
7775
7799
|
}
|
|
7776
7800
|
|
|
7777
7801
|
main().catch((err) => {
|
|
@@ -8714,12 +8738,16 @@ console.log('Connected to:', env.environmentId);`);
|
|
|
8714
8738
|
if (classes.length > 0) {
|
|
8715
8739
|
const cls = classes[0];
|
|
8716
8740
|
const ClassName = cls.name.charAt(0).toUpperCase() + cls.name.slice(1);
|
|
8717
|
-
lines.push(` // 1.
|
|
8718
|
-
lines.push(` const
|
|
8719
|
-
lines.push(`
|
|
8741
|
+
lines.push(` // 1. List objects`);
|
|
8742
|
+
lines.push(` const items = await ${ClassName}.list({ limit: 10, saveAs: '${cls.name}_items' });`);
|
|
8743
|
+
lines.push(` const item = items[0] ?? null;`);
|
|
8744
|
+
lines.push(` console.log('Loaded:', item?.id);`);
|
|
8720
8745
|
lines.push(``);
|
|
8721
8746
|
lines.push(` // 2. Call instance method`);
|
|
8722
|
-
lines.push(`
|
|
8747
|
+
lines.push(` if (item) {`);
|
|
8748
|
+
lines.push(` const summary = await item.summarize({ maxLength: 100 });`);
|
|
8749
|
+
lines.push(` console.log(summary);`);
|
|
8750
|
+
lines.push(` }`);
|
|
8723
8751
|
lines.push(``);
|
|
8724
8752
|
lines.push(` // 3. Call static method`);
|
|
8725
8753
|
lines.push(` const results = await ${ClassName}.search({ query: 'test' });`);
|
package/dist/index.d.mts
CHANGED
|
@@ -904,7 +904,8 @@ declare class Session {
|
|
|
904
904
|
* ```typescript
|
|
905
905
|
* import { Author, Book, global_search } from './sandbox-tools';
|
|
906
906
|
*
|
|
907
|
-
* const
|
|
907
|
+
* const authors = await Author.list({ limit: 10, saveAs: 'recent_authors' });
|
|
908
|
+
* const tolkien = await Author.get({ path: 'author_tolkien' });
|
|
908
909
|
* const bio = await tolkien.get_bio({ detailed: true });
|
|
909
910
|
* const books = await tolkien.get_books();
|
|
910
911
|
* ```
|
|
@@ -1410,6 +1411,13 @@ declare class Granular {
|
|
|
1410
1411
|
* sandbox-scoped live catalog.
|
|
1411
1412
|
*/
|
|
1412
1413
|
unregisterEffect(sandboxNameOrId: string, name: string): Promise<void>;
|
|
1414
|
+
/**
|
|
1415
|
+
* Disconnect one sandbox-scoped effect host, or all of them when no sandbox is provided.
|
|
1416
|
+
*
|
|
1417
|
+
* This is primarily useful for long-lived helper processes such as generated
|
|
1418
|
+
* `granular-effects.ts` scripts that need to shut down cleanly on SIGINT/SIGTERM.
|
|
1419
|
+
*/
|
|
1420
|
+
disconnectEffects(sandboxNameOrId?: string): Promise<void>;
|
|
1413
1421
|
/**
|
|
1414
1422
|
* Unregister all effects for a sandbox.
|
|
1415
1423
|
*/
|
package/dist/index.d.ts
CHANGED
|
@@ -904,7 +904,8 @@ declare class Session {
|
|
|
904
904
|
* ```typescript
|
|
905
905
|
* import { Author, Book, global_search } from './sandbox-tools';
|
|
906
906
|
*
|
|
907
|
-
* const
|
|
907
|
+
* const authors = await Author.list({ limit: 10, saveAs: 'recent_authors' });
|
|
908
|
+
* const tolkien = await Author.get({ path: 'author_tolkien' });
|
|
908
909
|
* const bio = await tolkien.get_bio({ detailed: true });
|
|
909
910
|
* const books = await tolkien.get_books();
|
|
910
911
|
* ```
|
|
@@ -1410,6 +1411,13 @@ declare class Granular {
|
|
|
1410
1411
|
* sandbox-scoped live catalog.
|
|
1411
1412
|
*/
|
|
1412
1413
|
unregisterEffect(sandboxNameOrId: string, name: string): Promise<void>;
|
|
1414
|
+
/**
|
|
1415
|
+
* Disconnect one sandbox-scoped effect host, or all of them when no sandbox is provided.
|
|
1416
|
+
*
|
|
1417
|
+
* This is primarily useful for long-lived helper processes such as generated
|
|
1418
|
+
* `granular-effects.ts` scripts that need to shut down cleanly on SIGINT/SIGTERM.
|
|
1419
|
+
*/
|
|
1420
|
+
disconnectEffects(sandboxNameOrId?: string): Promise<void>;
|
|
1413
1421
|
/**
|
|
1414
1422
|
* Unregister all effects for a sandbox.
|
|
1415
1423
|
*/
|
package/dist/index.js
CHANGED
|
@@ -4577,7 +4577,8 @@ var Session = class {
|
|
|
4577
4577
|
* ```typescript
|
|
4578
4578
|
* import { Author, Book, global_search } from './sandbox-tools';
|
|
4579
4579
|
*
|
|
4580
|
-
* const
|
|
4580
|
+
* const authors = await Author.list({ limit: 10, saveAs: 'recent_authors' });
|
|
4581
|
+
* const tolkien = await Author.get({ path: 'author_tolkien' });
|
|
4581
4582
|
* const bio = await tolkien.get_bio({ detailed: true });
|
|
4582
4583
|
* const books = await tolkien.get_books();
|
|
4583
4584
|
* ```
|
|
@@ -6149,7 +6150,21 @@ var Granular = class {
|
|
|
6149
6150
|
const effects = Array.from(this.getSandboxEffectMap(host.sandboxId).values()).map(
|
|
6150
6151
|
(effect) => this.serializeEffect(effect)
|
|
6151
6152
|
);
|
|
6152
|
-
await host.wsClient.call("effects.publishCatalog", { effects });
|
|
6153
|
+
const result = await host.wsClient.call("effects.publishCatalog", { effects });
|
|
6154
|
+
const acceptedCount = typeof result?.acceptedCount === "number" ? result.acceptedCount : 0;
|
|
6155
|
+
const rejected = Array.isArray(result?.rejected) ? result.rejected : [];
|
|
6156
|
+
if (acceptedCount === 0 && rejected.length > 0) {
|
|
6157
|
+
const detail = rejected.map((entry) => `${entry.name || "unknown"}: ${entry.reason || "rejected"}`).join("; ");
|
|
6158
|
+
throw new Error(
|
|
6159
|
+
`Failed to publish live effects for sandbox ${host.sandboxId}: ${detail}`
|
|
6160
|
+
);
|
|
6161
|
+
}
|
|
6162
|
+
if (rejected.length > 0) {
|
|
6163
|
+
console.warn(
|
|
6164
|
+
`[Granular] Some live effects were rejected for sandbox ${host.sandboxId}:`,
|
|
6165
|
+
rejected
|
|
6166
|
+
);
|
|
6167
|
+
}
|
|
6153
6168
|
}
|
|
6154
6169
|
async syncSandboxEffectCatalog(sandboxId) {
|
|
6155
6170
|
const host = await this.ensureSandboxEffectHost(sandboxId);
|
|
@@ -6315,6 +6330,22 @@ var Granular = class {
|
|
|
6315
6330
|
}
|
|
6316
6331
|
await this.syncSandboxEffectCatalog(sandboxId);
|
|
6317
6332
|
}
|
|
6333
|
+
/**
|
|
6334
|
+
* Disconnect one sandbox-scoped effect host, or all of them when no sandbox is provided.
|
|
6335
|
+
*
|
|
6336
|
+
* This is primarily useful for long-lived helper processes such as generated
|
|
6337
|
+
* `granular-effects.ts` scripts that need to shut down cleanly on SIGINT/SIGTERM.
|
|
6338
|
+
*/
|
|
6339
|
+
async disconnectEffects(sandboxNameOrId) {
|
|
6340
|
+
if (sandboxNameOrId) {
|
|
6341
|
+
const sandbox = await this.findOrCreateSandbox(sandboxNameOrId);
|
|
6342
|
+
this.disconnectSandboxEffectHost(sandbox.sandboxId);
|
|
6343
|
+
return;
|
|
6344
|
+
}
|
|
6345
|
+
for (const sandboxId of Array.from(this.sandboxEffectHosts.keys())) {
|
|
6346
|
+
this.disconnectSandboxEffectHost(sandboxId);
|
|
6347
|
+
}
|
|
6348
|
+
}
|
|
6318
6349
|
/**
|
|
6319
6350
|
* Unregister all effects for a sandbox.
|
|
6320
6351
|
*/
|