@granular-software/sdk 0.4.1 → 0.4.3
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 +18 -15
- package/dist/adapters/anthropic.d.mts +1 -1
- package/dist/adapters/anthropic.d.ts +1 -1
- package/dist/adapters/langchain.d.mts +1 -1
- package/dist/adapters/langchain.d.ts +1 -1
- package/dist/adapters/mastra.d.mts +1 -1
- package/dist/adapters/mastra.d.ts +1 -1
- package/dist/adapters/openai.d.mts +1 -1
- package/dist/adapters/openai.d.ts +1 -1
- package/dist/cli/index.js +137 -46
- package/dist/index.d.mts +51 -162
- package/dist/index.d.ts +51 -162
- package/dist/index.js +305 -265
- package/dist/index.js.map +1 -1
- package/dist/index.mjs +305 -265
- package/dist/index.mjs.map +1 -1
- package/dist/{types-BOPsFZYi.d.mts → types-C0AVRsVR.d.mts} +85 -31
- package/dist/{types-BOPsFZYi.d.ts → types-C0AVRsVR.d.ts} +85 -31
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -32,7 +32,7 @@ npm install @granular-software/sdk
|
|
|
32
32
|
By default, the SDK resolves endpoints like this:
|
|
33
33
|
|
|
34
34
|
- Local mode (`NODE_ENV=development`): `ws://localhost:8787/granular`
|
|
35
|
-
- Production mode (default): `wss://api.
|
|
35
|
+
- Production mode (default): `wss://cf-api-gateway.arthur6084.workers.dev/granular`
|
|
36
36
|
|
|
37
37
|
Overrides:
|
|
38
38
|
|
|
@@ -112,8 +112,8 @@ await env.recordObject({
|
|
|
112
112
|
fields: { name: 'Acme Corp', email: 'billing@acme.com', tier: 'enterprise' },
|
|
113
113
|
});
|
|
114
114
|
|
|
115
|
-
// 5.
|
|
116
|
-
await env.
|
|
115
|
+
// 5. Register live effect handlers for effects already declared in the build manifest
|
|
116
|
+
await granular.registerEffects(env.sandboxId, [
|
|
117
117
|
{
|
|
118
118
|
name: 'get_billing_summary',
|
|
119
119
|
description: 'Get billing summary for a customer',
|
|
@@ -134,8 +134,9 @@ await env.publishTools([
|
|
|
134
134
|
},
|
|
135
135
|
required: ['total', 'invoices'],
|
|
136
136
|
},
|
|
137
|
-
handler: async (customerId: string, params: any) => {
|
|
137
|
+
handler: async (customerId: string, params: any, ctx: any) => {
|
|
138
138
|
// customerId comes from `this.id` in sandbox code
|
|
139
|
+
console.log('Running for subject:', ctx.user.subjectId);
|
|
139
140
|
return { total: 4250.00, invoices: 3, period: params?.period || 'current' };
|
|
140
141
|
},
|
|
141
142
|
},
|
|
@@ -163,17 +164,19 @@ const job = await env.submitJob(`
|
|
|
163
164
|
const result = await job.result;
|
|
164
165
|
```
|
|
165
166
|
|
|
167
|
+
Effects must be declared ahead of time in the sandbox build manifest with `withEffect`. Live registration only makes already-declared effects available at runtime.
|
|
168
|
+
|
|
166
169
|
## Core Flow
|
|
167
170
|
|
|
168
171
|
```
|
|
169
|
-
|
|
172
|
+
declare effects in build manifest → connect() → recordObject() → registerEffects() → submitJob()
|
|
170
173
|
```
|
|
171
174
|
|
|
172
175
|
1. **`recordUser()`** — Register a user and their permission profiles
|
|
173
176
|
2. **`connect()`** — Connect to a sandbox, returning an `Environment`
|
|
174
177
|
3. **`applyManifest()`** — Define your domain ontology (classes, properties, relationships)
|
|
175
178
|
4. **`recordObject()`** — Create/update instances of your classes with fields and relationships
|
|
176
|
-
5. **`
|
|
179
|
+
5. **`granular.registerEffects()`** — Register sandbox-scoped live handlers for effects declared in the build manifest
|
|
177
180
|
6. **`submitJob()`** — Execute code in the sandbox that uses the auto-generated typed classes
|
|
178
181
|
|
|
179
182
|
## Defining the Domain Ontology
|
|
@@ -249,12 +252,12 @@ const lotr = await env.recordObject({
|
|
|
249
252
|
|
|
250
253
|
> **Cross-class ID uniqueness**: Two objects of different classes can share the same real-world ID (e.g., an `author` "tolkien" and a `publisher` "tolkien"). The SDK derives unique graph paths internally (`author_tolkien`, `publisher_tolkien`) so they never collide.
|
|
251
254
|
|
|
252
|
-
##
|
|
255
|
+
## Effect Definitions
|
|
253
256
|
|
|
254
|
-
|
|
257
|
+
Effects are declared in the manifest with `withEffect`, then their live handlers are registered at sandbox scope. Effects can be **instance methods**, **static methods**, or **global functions**. Both `inputSchema` and `outputSchema` use JSON Schema:
|
|
255
258
|
|
|
256
259
|
```typescript
|
|
257
|
-
await env.
|
|
260
|
+
await granular.registerEffects(env.sandboxId, [
|
|
258
261
|
// Instance method: called as `tolkien.get_bio({ detailed: true })`
|
|
259
262
|
// Handler receives (objectId, params)
|
|
260
263
|
{
|
|
@@ -276,7 +279,7 @@ await env.publishTools([
|
|
|
276
279
|
},
|
|
277
280
|
required: ['bio'],
|
|
278
281
|
},
|
|
279
|
-
handler: async (id: string, params: any) => {
|
|
282
|
+
handler: async (id: string, params: any, ctx: any) => {
|
|
280
283
|
return { bio: `Biography of ${id}`, source: 'database' };
|
|
281
284
|
},
|
|
282
285
|
},
|
|
@@ -297,7 +300,7 @@ await env.publishTools([
|
|
|
297
300
|
type: 'object',
|
|
298
301
|
properties: { results: { type: 'array' } },
|
|
299
302
|
},
|
|
300
|
-
handler: async (params: any) => {
|
|
303
|
+
handler: async (params: any, ctx: any) => {
|
|
301
304
|
return { results: [`Found: ${params.query}`] };
|
|
302
305
|
},
|
|
303
306
|
},
|
|
@@ -316,7 +319,7 @@ await env.publishTools([
|
|
|
316
319
|
type: 'object',
|
|
317
320
|
properties: { results: { type: 'array' } },
|
|
318
321
|
},
|
|
319
|
-
handler: async (params: any) => {
|
|
322
|
+
handler: async (params: any, ctx: any) => {
|
|
320
323
|
return { results: [`Result for: ${params.query}`] };
|
|
321
324
|
},
|
|
322
325
|
},
|
|
@@ -470,8 +473,8 @@ Returns relationship definitions for a given class.
|
|
|
470
473
|
### `environment.listRelated(modelPath, submodelPath)`
|
|
471
474
|
Lists related instances through a relationship.
|
|
472
475
|
|
|
473
|
-
### `
|
|
474
|
-
|
|
476
|
+
### `granular.registerEffects(sandboxId, effects)`
|
|
477
|
+
Registers sandbox-scoped live handlers for effects declared in the sandbox build manifest. Effects can be instance methods (`className` set, `static` omitted), static methods (`static: true`), or global functions (no `className`).
|
|
475
478
|
|
|
476
479
|
### `environment.submitJob(code)`
|
|
477
480
|
Submits code to be executed in the sandbox. The code imports typed classes from `./sandbox-tools`.
|
|
@@ -483,7 +486,7 @@ Get auto-generated TypeScript class declarations. Pass this to LLMs to help them
|
|
|
483
486
|
Execute a GraphQL query against the environment's graph. Authenticated automatically.
|
|
484
487
|
|
|
485
488
|
### `environment.on(event, handler)`
|
|
486
|
-
Listen for events: `'
|
|
489
|
+
Listen for events: `'effect:invoke'`, `'effect:result'`, `'job:status'`, `'stdout'`, etc. Legacy `'tool:*'` aliases still exist internally but are no longer the primary model.
|
|
487
490
|
|
|
488
491
|
### `Environment.toGraphPath(className, id)`
|
|
489
492
|
Convert a class name + real-world ID to a unique graph path (`{className}_{id}`).
|
package/dist/cli/index.js
CHANGED
|
@@ -5387,7 +5387,7 @@ var import_dotenv = __toESM(require_main());
|
|
|
5387
5387
|
|
|
5388
5388
|
// src/endpoints.ts
|
|
5389
5389
|
var LOCAL_API_URL = "ws://localhost:8787/granular";
|
|
5390
|
-
var PRODUCTION_API_URL = "wss://api.
|
|
5390
|
+
var PRODUCTION_API_URL = "wss://cf-api-gateway.arthur6084.workers.dev/granular";
|
|
5391
5391
|
var LOCAL_AUTH_URL = "http://localhost:3000";
|
|
5392
5392
|
var PRODUCTION_AUTH_URL = "https://app.granular.software";
|
|
5393
5393
|
function readEnv(name) {
|
|
@@ -5552,7 +5552,105 @@ function ensureGitignore() {
|
|
|
5552
5552
|
fs__namespace.writeFileSync(gitignorePath, content + section, "utf-8");
|
|
5553
5553
|
}
|
|
5554
5554
|
}
|
|
5555
|
+
function createDefaultEffectOperations(classNames) {
|
|
5556
|
+
const operations = classNames.flatMap((className) => {
|
|
5557
|
+
const searchName = `search_${className}s`;
|
|
5558
|
+
return [
|
|
5559
|
+
{
|
|
5560
|
+
withEffect: {
|
|
5561
|
+
name: "get_info",
|
|
5562
|
+
description: `Get details of a ${className} by ID`,
|
|
5563
|
+
attachedClass: className,
|
|
5564
|
+
isStatic: false,
|
|
5565
|
+
inputSchema: {
|
|
5566
|
+
type: "object",
|
|
5567
|
+
properties: {
|
|
5568
|
+
include_related: { type: "boolean", description: "Include related items" }
|
|
5569
|
+
}
|
|
5570
|
+
},
|
|
5571
|
+
outputSchema: {
|
|
5572
|
+
type: "object",
|
|
5573
|
+
properties: {
|
|
5574
|
+
id: { type: "string" },
|
|
5575
|
+
name: { type: "string" },
|
|
5576
|
+
related: { type: "array", items: { type: "object" } }
|
|
5577
|
+
}
|
|
5578
|
+
}
|
|
5579
|
+
}
|
|
5580
|
+
},
|
|
5581
|
+
{
|
|
5582
|
+
withEffect: {
|
|
5583
|
+
name: searchName,
|
|
5584
|
+
description: `Search ${className}s by keyword`,
|
|
5585
|
+
attachedClass: className,
|
|
5586
|
+
isStatic: true,
|
|
5587
|
+
inputSchema: {
|
|
5588
|
+
type: "object",
|
|
5589
|
+
properties: {
|
|
5590
|
+
query: { type: "string", description: "Search keyword" },
|
|
5591
|
+
limit: { type: "number", description: "Max results" }
|
|
5592
|
+
},
|
|
5593
|
+
required: ["query"]
|
|
5594
|
+
},
|
|
5595
|
+
outputSchema: {
|
|
5596
|
+
type: "object",
|
|
5597
|
+
properties: {
|
|
5598
|
+
query: { type: "string" },
|
|
5599
|
+
results: {
|
|
5600
|
+
type: "array",
|
|
5601
|
+
items: {
|
|
5602
|
+
type: "object",
|
|
5603
|
+
properties: {
|
|
5604
|
+
id: { type: "string" },
|
|
5605
|
+
name: { type: "string" }
|
|
5606
|
+
}
|
|
5607
|
+
}
|
|
5608
|
+
},
|
|
5609
|
+
total: { type: "number" }
|
|
5610
|
+
}
|
|
5611
|
+
}
|
|
5612
|
+
}
|
|
5613
|
+
}
|
|
5614
|
+
];
|
|
5615
|
+
});
|
|
5616
|
+
operations.push({
|
|
5617
|
+
withEffect: {
|
|
5618
|
+
name: "full_text_search",
|
|
5619
|
+
description: "Search across all types",
|
|
5620
|
+
inputSchema: {
|
|
5621
|
+
type: "object",
|
|
5622
|
+
properties: {
|
|
5623
|
+
query: { type: "string", description: "Search query" },
|
|
5624
|
+
types: { type: "array", items: { type: "string" }, description: "Filter by type" }
|
|
5625
|
+
},
|
|
5626
|
+
required: ["query"]
|
|
5627
|
+
},
|
|
5628
|
+
outputSchema: {
|
|
5629
|
+
type: "object",
|
|
5630
|
+
properties: {
|
|
5631
|
+
query: { type: "string" },
|
|
5632
|
+
types: { type: "array", items: { type: "string" } },
|
|
5633
|
+
matches: {
|
|
5634
|
+
type: "array",
|
|
5635
|
+
items: {
|
|
5636
|
+
type: "object",
|
|
5637
|
+
properties: {
|
|
5638
|
+
type: { type: "string" },
|
|
5639
|
+
id: { type: "string" },
|
|
5640
|
+
label: { type: "string" },
|
|
5641
|
+
snippet: { type: "string" }
|
|
5642
|
+
}
|
|
5643
|
+
}
|
|
5644
|
+
},
|
|
5645
|
+
total: { type: "number" }
|
|
5646
|
+
}
|
|
5647
|
+
}
|
|
5648
|
+
}
|
|
5649
|
+
});
|
|
5650
|
+
return operations;
|
|
5651
|
+
}
|
|
5555
5652
|
function createDefaultManifest(name) {
|
|
5653
|
+
const classNames = ["note", "tag"];
|
|
5556
5654
|
return {
|
|
5557
5655
|
manifest: {
|
|
5558
5656
|
schemaVersion: 2,
|
|
@@ -5591,7 +5689,8 @@ function createDefaultManifest(name) {
|
|
|
5591
5689
|
leftIsMany: true,
|
|
5592
5690
|
rightIsMany: true
|
|
5593
5691
|
}
|
|
5594
|
-
}
|
|
5692
|
+
},
|
|
5693
|
+
...createDefaultEffectOperations(classNames)
|
|
5595
5694
|
]
|
|
5596
5695
|
}]
|
|
5597
5696
|
}
|
|
@@ -5676,6 +5775,9 @@ var ApiClient = class {
|
|
|
5676
5775
|
return false;
|
|
5677
5776
|
}
|
|
5678
5777
|
}
|
|
5778
|
+
async validateKeyOrThrow() {
|
|
5779
|
+
await this.request("/control/sandboxes");
|
|
5780
|
+
}
|
|
5679
5781
|
// ── Sandboxes ──
|
|
5680
5782
|
async listSandboxes() {
|
|
5681
5783
|
const result = await this.request("/control/sandboxes");
|
|
@@ -5762,7 +5864,7 @@ var ApiClient = class {
|
|
|
5762
5864
|
return await this.createPermissionProfile(sandboxId, {
|
|
5763
5865
|
name: "default",
|
|
5764
5866
|
rules: {
|
|
5765
|
-
|
|
5867
|
+
effects: { allow: ["*"] },
|
|
5766
5868
|
resources: { allow: ["*"] }
|
|
5767
5869
|
}
|
|
5768
5870
|
});
|
|
@@ -7663,37 +7765,12 @@ async function main() {
|
|
|
7663
7765
|
apiUrl: process.env.GRANULAR_API_URL ?? API_URL,
|
|
7664
7766
|
});
|
|
7665
7767
|
|
|
7666
|
-
|
|
7667
|
-
log(\`Recording user \${userId}\`);
|
|
7668
|
-
const user = await granular.recordUser({
|
|
7669
|
-
userId,
|
|
7670
|
-
name: 'Effects Host',
|
|
7671
|
-
permissions: ['default'],
|
|
7672
|
-
});
|
|
7673
|
-
|
|
7674
|
-
log(\`Connecting to sandbox \${SANDBOX_ID}\`);
|
|
7675
|
-
const env = await granular.connect({ sandbox: SANDBOX_ID, user, clientId: 'effects-host' });
|
|
7676
|
-
|
|
7677
|
-
log('Waiting for graph container to be ready...');
|
|
7678
|
-
for (let i = 0; i < 6; i++) {
|
|
7679
|
-
await new Promise((r) => setTimeout(r, 5000));
|
|
7680
|
-
try {
|
|
7681
|
-
const probe = await env.graphql(\`query { model(path: "class") { path } }\`);
|
|
7682
|
-
if ((probe as any)?.data?.model?.path) {
|
|
7683
|
-
log(\`Graph ready after \${(i + 1) * 5}s\`);
|
|
7684
|
-
break;
|
|
7685
|
-
}
|
|
7686
|
-
} catch {
|
|
7687
|
-
log(\`Not ready yet (\${(i + 1) * 5}s)...\`);
|
|
7688
|
-
}
|
|
7689
|
-
}
|
|
7690
|
-
|
|
7691
|
-
log('Publishing effects...');
|
|
7768
|
+
log(\`Registering live effects for sandbox \${SANDBOX_ID}\`);
|
|
7692
7769
|
${mockDataBlock}
|
|
7693
7770
|
|
|
7694
|
-
await
|
|
7771
|
+
await granular.registerEffects(SANDBOX_ID, ${effectsArray});
|
|
7695
7772
|
|
|
7696
|
-
log('Effects
|
|
7773
|
+
log('Effects registered. Process kept alive for simulator. Press Ctrl+C to exit.');
|
|
7697
7774
|
await new Promise(() => {});
|
|
7698
7775
|
}
|
|
7699
7776
|
|
|
@@ -7773,9 +7850,11 @@ async function initCommand(projectName, options) {
|
|
|
7773
7850
|
const apiUrl = loadApiUrl();
|
|
7774
7851
|
const api = new ApiClient(apiKey, apiUrl);
|
|
7775
7852
|
const validating = spinner("Validating API key...");
|
|
7776
|
-
|
|
7777
|
-
|
|
7778
|
-
|
|
7853
|
+
try {
|
|
7854
|
+
await api.validateKeyOrThrow();
|
|
7855
|
+
} catch (err) {
|
|
7856
|
+
const detail = err?.message ? ` (${err.message})` : "";
|
|
7857
|
+
validating.fail(` API key validation failed${detail}.`);
|
|
7779
7858
|
process.exit(1);
|
|
7780
7859
|
}
|
|
7781
7860
|
validating.succeed(" API key validated.");
|
|
@@ -7858,6 +7937,7 @@ async function initCommand(projectName, options) {
|
|
|
7858
7937
|
hint("granular simulate", "opens app.granular.software/simulator for this sandbox");
|
|
7859
7938
|
console.log();
|
|
7860
7939
|
}
|
|
7940
|
+
var DEFAULT_LOCAL_API_KEY = "gn_sk_tenant_default_principal_local_e2e_00000000";
|
|
7861
7941
|
function promptSecret(question) {
|
|
7862
7942
|
const rl = readline__namespace.createInterface({ input: process.stdin, output: process.stdout });
|
|
7863
7943
|
return new Promise((resolve) => {
|
|
@@ -7883,6 +7963,9 @@ function maskApiKey(apiKey) {
|
|
|
7883
7963
|
if (apiKey.length < 10) return `${apiKey}...`;
|
|
7884
7964
|
return `${apiKey.substring(0, 10)}...`;
|
|
7885
7965
|
}
|
|
7966
|
+
function isLocalApiUrl(apiUrl) {
|
|
7967
|
+
return apiUrl.startsWith("ws://localhost:") || apiUrl.startsWith("wss://localhost:") || apiUrl.startsWith("ws://127.0.0.1:") || apiUrl.startsWith("wss://127.0.0.1:") || apiUrl.startsWith("http://localhost:") || apiUrl.startsWith("https://localhost:") || apiUrl.startsWith("http://127.0.0.1:") || apiUrl.startsWith("https://127.0.0.1:");
|
|
7968
|
+
}
|
|
7886
7969
|
async function loginCommand(options = {}) {
|
|
7887
7970
|
printHeader();
|
|
7888
7971
|
const existing = loadApiKey();
|
|
@@ -7894,7 +7977,10 @@ async function loginCommand(options = {}) {
|
|
|
7894
7977
|
const apiUrl = loadApiUrl();
|
|
7895
7978
|
let apiKey = options.apiKey?.trim();
|
|
7896
7979
|
if (!apiKey) {
|
|
7897
|
-
if (options.manual) {
|
|
7980
|
+
if (!options.manual && isLocalApiUrl(apiUrl)) {
|
|
7981
|
+
apiKey = existing?.trim() || process.env.GRANULAR_LOCAL_API_KEY?.trim() || DEFAULT_LOCAL_API_KEY;
|
|
7982
|
+
info("Using local Granular API key for localhost development.");
|
|
7983
|
+
} else if (options.manual) {
|
|
7898
7984
|
dim("Get your API key at https://app.granular.software/w/default/api-keys");
|
|
7899
7985
|
console.log();
|
|
7900
7986
|
apiKey = await promptSecret("Enter your API key");
|
|
@@ -7934,9 +8020,11 @@ async function loginCommand(options = {}) {
|
|
|
7934
8020
|
}
|
|
7935
8021
|
const spinner2 = spinner("Validating...");
|
|
7936
8022
|
const api = new ApiClient(apiKey, apiUrl);
|
|
7937
|
-
|
|
7938
|
-
|
|
7939
|
-
|
|
8023
|
+
try {
|
|
8024
|
+
await api.validateKeyOrThrow();
|
|
8025
|
+
} catch (err) {
|
|
8026
|
+
const detail = err?.message ? ` (${err.message})` : "";
|
|
8027
|
+
spinner2.fail(` API key validation failed${detail}.`);
|
|
7940
8028
|
process.exit(1);
|
|
7941
8029
|
}
|
|
7942
8030
|
saveApiKey(apiKey);
|
|
@@ -8448,7 +8536,7 @@ ${manifest.description}`);
|
|
|
8448
8536
|
lines.push(`- [Integration Guide](#integration-guide)`);
|
|
8449
8537
|
lines.push(` - [1. Initialize & Connect](#1-initialize--connect)`);
|
|
8450
8538
|
lines.push(` - [2. Record Objects](#2-record-objects)`);
|
|
8451
|
-
lines.push(` - [3.
|
|
8539
|
+
lines.push(` - [3. Declare And Register Effects](#3-declare-and-register-effects)`);
|
|
8452
8540
|
lines.push(` - [4. Submit Jobs](#4-submit-jobs)`);
|
|
8453
8541
|
lines.push(`
|
|
8454
8542
|
## Getting Started`);
|
|
@@ -8564,11 +8652,11 @@ console.log('Connected to:', env.environmentId);`);
|
|
|
8564
8652
|
lines.push(`*(No classes defined in manifest)*`);
|
|
8565
8653
|
}
|
|
8566
8654
|
lines.push(`
|
|
8567
|
-
### 3.
|
|
8568
|
-
lines.push(`
|
|
8655
|
+
### 3. Declare And Register Effects`);
|
|
8656
|
+
lines.push(`Declare effects in your manifest with \`withEffect\`, then register live handlers at sandbox scope.`);
|
|
8569
8657
|
lines.push(`
|
|
8570
8658
|
\`\`\`typescript`);
|
|
8571
|
-
lines.push(`await
|
|
8659
|
+
lines.push(`await granular.registerEffects('your-sandbox-id', [`);
|
|
8572
8660
|
if (classes.length > 0) {
|
|
8573
8661
|
const cls = classes[0];
|
|
8574
8662
|
lines.push(` // Instance method on ${cls.name}`);
|
|
@@ -8578,8 +8666,9 @@ console.log('Connected to:', env.environmentId);`);
|
|
|
8578
8666
|
lines.push(` className: '${cls.name}',`);
|
|
8579
8667
|
lines.push(` inputSchema: { type: 'object', properties: { maxLength: { type: 'number' } } },`);
|
|
8580
8668
|
lines.push(` outputSchema: { type: 'object', properties: { summary: { type: 'string' } }, required: ['summary'] },`);
|
|
8581
|
-
lines.push(` handler: async (id, params) => {`);
|
|
8669
|
+
lines.push(` handler: async (id, params, ctx) => {`);
|
|
8582
8670
|
lines.push(` // 'id' is the real-world ID of the ${cls.name}`);
|
|
8671
|
+
lines.push(` console.log('Invoked for user', ctx.user.subjectId);`);
|
|
8583
8672
|
lines.push(` return { summary: \`Summary for \${id}\` };`);
|
|
8584
8673
|
lines.push(` },`);
|
|
8585
8674
|
lines.push(` },`);
|
|
@@ -8591,17 +8680,19 @@ console.log('Connected to:', env.environmentId);`);
|
|
|
8591
8680
|
lines.push(` static: true,`);
|
|
8592
8681
|
lines.push(` inputSchema: { type: 'object', properties: { query: { type: 'string' } }, required: ['query'] },`);
|
|
8593
8682
|
lines.push(` outputSchema: { type: 'object', properties: { ids: { type: 'array' } }, required: ['ids'] },`);
|
|
8594
|
-
lines.push(` handler: async (params) => {`);
|
|
8683
|
+
lines.push(` handler: async (params, ctx) => {`);
|
|
8684
|
+
lines.push(` console.log('Invoked for user', ctx.user.subjectId);`);
|
|
8595
8685
|
lines.push(` return { ids: [] };`);
|
|
8596
8686
|
lines.push(` },`);
|
|
8597
8687
|
lines.push(` },`);
|
|
8598
8688
|
}
|
|
8599
|
-
lines.push(` // Global
|
|
8689
|
+
lines.push(` // Global effect`);
|
|
8600
8690
|
lines.push(` {`);
|
|
8601
8691
|
lines.push(` name: 'notify',`);
|
|
8602
8692
|
lines.push(` description: 'Send notification',`);
|
|
8603
8693
|
lines.push(` inputSchema: { type: 'object', properties: { msg: { type: 'string' } }, required: ['msg'] },`);
|
|
8604
|
-
lines.push(` handler: async (params) => {`);
|
|
8694
|
+
lines.push(` handler: async (params, ctx) => {`);
|
|
8695
|
+
lines.push(` console.log('Invoked for user', ctx.user.subjectId);`);
|
|
8605
8696
|
lines.push(` console.log('Notification:', params.msg);`);
|
|
8606
8697
|
lines.push(` },`);
|
|
8607
8698
|
lines.push(` },`);
|