@granular-software/sdk 0.4.5 → 0.4.7
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 +34 -19
- package/dist/cli/index.js +43 -14
- package/dist/index.d.mts +45 -12
- package/dist/index.d.ts +45 -12
- package/dist/index.js +120 -23
- package/dist/index.js.map +1 -1
- package/dist/index.mjs +120 -23
- package/dist/index.mjs.map +1 -1
- package/package.json +1 -1
package/dist/index.mjs
CHANGED
|
@@ -4481,6 +4481,8 @@ var Session = class {
|
|
|
4481
4481
|
environmentId: "",
|
|
4482
4482
|
sessionId: "",
|
|
4483
4483
|
user: {
|
|
4484
|
+
granularId: "",
|
|
4485
|
+
userId: "",
|
|
4484
4486
|
subjectId: ""
|
|
4485
4487
|
}
|
|
4486
4488
|
};
|
|
@@ -4555,7 +4557,8 @@ var Session = class {
|
|
|
4555
4557
|
* ```typescript
|
|
4556
4558
|
* import { Author, Book, global_search } from './sandbox-tools';
|
|
4557
4559
|
*
|
|
4558
|
-
* const
|
|
4560
|
+
* const authors = await Author.list({ limit: 10, saveAs: 'recent_authors' });
|
|
4561
|
+
* const tolkien = await Author.get({ path: 'author_tolkien' });
|
|
4559
4562
|
* const bio = await tolkien.get_bio({ detailed: true });
|
|
4560
4563
|
* const books = await tolkien.get_books();
|
|
4561
4564
|
* ```
|
|
@@ -5174,6 +5177,29 @@ function normalizeHeapSnapshot(raw) {
|
|
|
5174
5177
|
updatedAt: typeof heap.updatedAt === "number" ? heap.updatedAt : Date.now()
|
|
5175
5178
|
};
|
|
5176
5179
|
}
|
|
5180
|
+
function normalizeSubject(subject) {
|
|
5181
|
+
const granularId = subject.granularId || subject.subjectId;
|
|
5182
|
+
const userId = subject.userId || subject.identityId || granularId;
|
|
5183
|
+
return {
|
|
5184
|
+
...subject,
|
|
5185
|
+
granularId,
|
|
5186
|
+
userId,
|
|
5187
|
+
subjectId: granularId,
|
|
5188
|
+
identityId: subject.identityId || userId
|
|
5189
|
+
};
|
|
5190
|
+
}
|
|
5191
|
+
function normalizeUser(user) {
|
|
5192
|
+
const granularId = user.granularId || user.subjectId;
|
|
5193
|
+
const userId = user.userId || user.identityId || granularId;
|
|
5194
|
+
return {
|
|
5195
|
+
...user,
|
|
5196
|
+
granularId,
|
|
5197
|
+
userId,
|
|
5198
|
+
subjectId: granularId,
|
|
5199
|
+
identityId: user.identityId || userId,
|
|
5200
|
+
permissions: Array.isArray(user.permissions) ? user.permissions : []
|
|
5201
|
+
};
|
|
5202
|
+
}
|
|
5177
5203
|
var Environment = class _Environment extends Session {
|
|
5178
5204
|
envData;
|
|
5179
5205
|
_apiKey;
|
|
@@ -5196,6 +5222,10 @@ var Environment = class _Environment extends Session {
|
|
|
5196
5222
|
get subjectId() {
|
|
5197
5223
|
return this.envData.subjectId;
|
|
5198
5224
|
}
|
|
5225
|
+
/** Internal Granular user identifier for this environment */
|
|
5226
|
+
get granularId() {
|
|
5227
|
+
return this.envData.subjectId;
|
|
5228
|
+
}
|
|
5199
5229
|
/** The permission profile ID */
|
|
5200
5230
|
get permissionProfileId() {
|
|
5201
5231
|
return this.envData.permissionProfileId;
|
|
@@ -5993,7 +6023,7 @@ var Granular = class {
|
|
|
5993
6023
|
* Records/upserts a user and prepares them for sandbox connections
|
|
5994
6024
|
*
|
|
5995
6025
|
* @param options - User options
|
|
5996
|
-
* @returns The user
|
|
6026
|
+
* @returns The recorded user with both `userId` and `granularId`
|
|
5997
6027
|
*
|
|
5998
6028
|
* @example
|
|
5999
6029
|
* ```typescript
|
|
@@ -6005,21 +6035,61 @@ var Granular = class {
|
|
|
6005
6035
|
* ```
|
|
6006
6036
|
*/
|
|
6007
6037
|
async recordUser(options) {
|
|
6008
|
-
const subject = await this.request("/control/subjects", {
|
|
6038
|
+
const subject = normalizeSubject(await this.request("/control/subjects", {
|
|
6009
6039
|
method: "POST",
|
|
6010
6040
|
body: JSON.stringify({
|
|
6011
6041
|
identityId: options.userId,
|
|
6012
6042
|
name: options.name,
|
|
6013
6043
|
email: options.email
|
|
6014
6044
|
})
|
|
6015
|
-
});
|
|
6016
|
-
return {
|
|
6045
|
+
}));
|
|
6046
|
+
return normalizeUser({
|
|
6047
|
+
granularId: subject.granularId,
|
|
6048
|
+
userId: options.userId,
|
|
6017
6049
|
subjectId: subject.subjectId,
|
|
6018
|
-
identityId:
|
|
6019
|
-
name: options.name,
|
|
6020
|
-
email: options.email,
|
|
6050
|
+
identityId: subject.identityId,
|
|
6051
|
+
name: options.name || subject.name || void 0,
|
|
6052
|
+
email: options.email || subject.email || void 0,
|
|
6021
6053
|
permissions: options.permissions || []
|
|
6022
|
-
};
|
|
6054
|
+
});
|
|
6055
|
+
}
|
|
6056
|
+
async resolveConnectUser(options) {
|
|
6057
|
+
if (options.user) {
|
|
6058
|
+
const user = normalizeUser(options.user);
|
|
6059
|
+
return {
|
|
6060
|
+
granularId: user.granularId,
|
|
6061
|
+
userId: user.userId,
|
|
6062
|
+
name: options.name || user.name,
|
|
6063
|
+
email: options.email || user.email,
|
|
6064
|
+
permissions: options.permissions || user.permissions
|
|
6065
|
+
};
|
|
6066
|
+
}
|
|
6067
|
+
if (options.userId) {
|
|
6068
|
+
const user = await this.recordUser({
|
|
6069
|
+
userId: options.userId,
|
|
6070
|
+
name: options.name,
|
|
6071
|
+
email: options.email,
|
|
6072
|
+
permissions: options.permissions
|
|
6073
|
+
});
|
|
6074
|
+
return {
|
|
6075
|
+
granularId: user.granularId,
|
|
6076
|
+
userId: user.userId,
|
|
6077
|
+
name: user.name,
|
|
6078
|
+
email: user.email,
|
|
6079
|
+
permissions: options.permissions || user.permissions
|
|
6080
|
+
};
|
|
6081
|
+
}
|
|
6082
|
+
if (options.granularId) {
|
|
6083
|
+
const subject = await this.subjects.get(options.granularId);
|
|
6084
|
+
return {
|
|
6085
|
+
granularId: subject.granularId,
|
|
6086
|
+
userId: subject.userId,
|
|
6087
|
+
name: options.name || subject.name || void 0,
|
|
6088
|
+
email: options.email || subject.email || void 0,
|
|
6089
|
+
permissions: options.permissions || []
|
|
6090
|
+
};
|
|
6091
|
+
}
|
|
6092
|
+
throw new Error("connect() requires either userId, granularId, or a user object returned by recordUser().");
|
|
6023
6093
|
}
|
|
6024
6094
|
/**
|
|
6025
6095
|
* Connect to a sandbox and establish a real-time environment session.
|
|
@@ -6033,14 +6103,10 @@ var Granular = class {
|
|
|
6033
6103
|
*
|
|
6034
6104
|
* @example
|
|
6035
6105
|
* ```typescript
|
|
6036
|
-
* const user = await granular.recordUser({
|
|
6037
|
-
* userId: 'user_123',
|
|
6038
|
-
* permissions: ['agent'],
|
|
6039
|
-
* });
|
|
6040
|
-
*
|
|
6041
6106
|
* const environment = await granular.connect({
|
|
6042
6107
|
* sandbox: 'my-sandbox',
|
|
6043
|
-
*
|
|
6108
|
+
* userId: 'user_123',
|
|
6109
|
+
* permissions: ['agent'],
|
|
6044
6110
|
* });
|
|
6045
6111
|
*
|
|
6046
6112
|
* await granular.registerEffect('my-sandbox', {
|
|
@@ -6061,13 +6127,14 @@ var Granular = class {
|
|
|
6061
6127
|
*/
|
|
6062
6128
|
async connect(options) {
|
|
6063
6129
|
const clientId = options.clientId || `client_${Date.now()}`;
|
|
6130
|
+
const user = await this.resolveConnectUser(options);
|
|
6064
6131
|
const sandbox = await this.findOrCreateSandbox(options.sandbox);
|
|
6065
|
-
for (const profileName of
|
|
6132
|
+
for (const profileName of user.permissions) {
|
|
6066
6133
|
const profileId = await this.ensurePermissionProfile(sandbox.sandboxId, profileName);
|
|
6067
|
-
await this.ensureAssignment(
|
|
6134
|
+
await this.ensureAssignment(user.granularId, sandbox.sandboxId, profileId);
|
|
6068
6135
|
}
|
|
6069
6136
|
const envData = await this.environments.create(sandbox.sandboxId, {
|
|
6070
|
-
subjectId:
|
|
6137
|
+
subjectId: user.granularId,
|
|
6071
6138
|
permissionProfileId: null
|
|
6072
6139
|
});
|
|
6073
6140
|
await this.activateEnvironment(envData.environmentId);
|
|
@@ -6127,7 +6194,21 @@ var Granular = class {
|
|
|
6127
6194
|
const effects = Array.from(this.getSandboxEffectMap(host.sandboxId).values()).map(
|
|
6128
6195
|
(effect) => this.serializeEffect(effect)
|
|
6129
6196
|
);
|
|
6130
|
-
await host.wsClient.call("effects.publishCatalog", { effects });
|
|
6197
|
+
const result = await host.wsClient.call("effects.publishCatalog", { effects });
|
|
6198
|
+
const acceptedCount = typeof result?.acceptedCount === "number" ? result.acceptedCount : 0;
|
|
6199
|
+
const rejected = Array.isArray(result?.rejected) ? result.rejected : [];
|
|
6200
|
+
if (acceptedCount === 0 && rejected.length > 0) {
|
|
6201
|
+
const detail = rejected.map((entry) => `${entry.name || "unknown"}: ${entry.reason || "rejected"}`).join("; ");
|
|
6202
|
+
throw new Error(
|
|
6203
|
+
`Failed to publish live effects for sandbox ${host.sandboxId}: ${detail}`
|
|
6204
|
+
);
|
|
6205
|
+
}
|
|
6206
|
+
if (rejected.length > 0) {
|
|
6207
|
+
console.warn(
|
|
6208
|
+
`[Granular] Some live effects were rejected for sandbox ${host.sandboxId}:`,
|
|
6209
|
+
rejected
|
|
6210
|
+
);
|
|
6211
|
+
}
|
|
6131
6212
|
}
|
|
6132
6213
|
async syncSandboxEffectCatalog(sandboxId) {
|
|
6133
6214
|
const host = await this.ensureSandboxEffectHost(sandboxId);
|
|
@@ -6293,6 +6374,22 @@ var Granular = class {
|
|
|
6293
6374
|
}
|
|
6294
6375
|
await this.syncSandboxEffectCatalog(sandboxId);
|
|
6295
6376
|
}
|
|
6377
|
+
/**
|
|
6378
|
+
* Disconnect one sandbox-scoped effect host, or all of them when no sandbox is provided.
|
|
6379
|
+
*
|
|
6380
|
+
* This is primarily useful for long-lived helper processes such as generated
|
|
6381
|
+
* `granular-effects.ts` scripts that need to shut down cleanly on SIGINT/SIGTERM.
|
|
6382
|
+
*/
|
|
6383
|
+
async disconnectEffects(sandboxNameOrId) {
|
|
6384
|
+
if (sandboxNameOrId) {
|
|
6385
|
+
const sandbox = await this.findOrCreateSandbox(sandboxNameOrId);
|
|
6386
|
+
this.disconnectSandboxEffectHost(sandbox.sandboxId);
|
|
6387
|
+
return;
|
|
6388
|
+
}
|
|
6389
|
+
for (const sandboxId of Array.from(this.sandboxEffectHosts.keys())) {
|
|
6390
|
+
this.disconnectSandboxEffectHost(sandboxId);
|
|
6391
|
+
}
|
|
6392
|
+
}
|
|
6296
6393
|
/**
|
|
6297
6394
|
* Unregister all effects for a sandbox.
|
|
6298
6395
|
*/
|
|
@@ -6470,7 +6567,7 @@ var Granular = class {
|
|
|
6470
6567
|
get subjects() {
|
|
6471
6568
|
return {
|
|
6472
6569
|
get: async (subjectId) => {
|
|
6473
|
-
return this.request(`/control/subjects/${subjectId}`);
|
|
6570
|
+
return normalizeSubject(await this.request(`/control/subjects/${subjectId}`));
|
|
6474
6571
|
},
|
|
6475
6572
|
listAssignments: async (subjectId) => {
|
|
6476
6573
|
return this.request(`/control/subjects/${subjectId}/assignments`);
|
|
@@ -6483,17 +6580,17 @@ var Granular = class {
|
|
|
6483
6580
|
get users() {
|
|
6484
6581
|
return {
|
|
6485
6582
|
create: async (data) => {
|
|
6486
|
-
return this.request("/control/subjects", {
|
|
6583
|
+
return normalizeSubject(await this.request("/control/subjects", {
|
|
6487
6584
|
method: "POST",
|
|
6488
6585
|
body: JSON.stringify({
|
|
6489
6586
|
identityId: data.id,
|
|
6490
6587
|
name: data.name,
|
|
6491
6588
|
email: data.email
|
|
6492
6589
|
})
|
|
6493
|
-
});
|
|
6590
|
+
}));
|
|
6494
6591
|
},
|
|
6495
6592
|
get: async (id) => {
|
|
6496
|
-
return this.request(`/control/subjects/${id}`);
|
|
6593
|
+
return normalizeSubject(await this.request(`/control/subjects/${id}`));
|
|
6497
6594
|
}
|
|
6498
6595
|
};
|
|
6499
6596
|
}
|