@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.js
CHANGED
|
@@ -4503,6 +4503,8 @@ var Session = class {
|
|
|
4503
4503
|
environmentId: "",
|
|
4504
4504
|
sessionId: "",
|
|
4505
4505
|
user: {
|
|
4506
|
+
granularId: "",
|
|
4507
|
+
userId: "",
|
|
4506
4508
|
subjectId: ""
|
|
4507
4509
|
}
|
|
4508
4510
|
};
|
|
@@ -4577,7 +4579,8 @@ var Session = class {
|
|
|
4577
4579
|
* ```typescript
|
|
4578
4580
|
* import { Author, Book, global_search } from './sandbox-tools';
|
|
4579
4581
|
*
|
|
4580
|
-
* const
|
|
4582
|
+
* const authors = await Author.list({ limit: 10, saveAs: 'recent_authors' });
|
|
4583
|
+
* const tolkien = await Author.get({ path: 'author_tolkien' });
|
|
4581
4584
|
* const bio = await tolkien.get_bio({ detailed: true });
|
|
4582
4585
|
* const books = await tolkien.get_books();
|
|
4583
4586
|
* ```
|
|
@@ -5196,6 +5199,29 @@ function normalizeHeapSnapshot(raw) {
|
|
|
5196
5199
|
updatedAt: typeof heap.updatedAt === "number" ? heap.updatedAt : Date.now()
|
|
5197
5200
|
};
|
|
5198
5201
|
}
|
|
5202
|
+
function normalizeSubject(subject) {
|
|
5203
|
+
const granularId = subject.granularId || subject.subjectId;
|
|
5204
|
+
const userId = subject.userId || subject.identityId || granularId;
|
|
5205
|
+
return {
|
|
5206
|
+
...subject,
|
|
5207
|
+
granularId,
|
|
5208
|
+
userId,
|
|
5209
|
+
subjectId: granularId,
|
|
5210
|
+
identityId: subject.identityId || userId
|
|
5211
|
+
};
|
|
5212
|
+
}
|
|
5213
|
+
function normalizeUser(user) {
|
|
5214
|
+
const granularId = user.granularId || user.subjectId;
|
|
5215
|
+
const userId = user.userId || user.identityId || granularId;
|
|
5216
|
+
return {
|
|
5217
|
+
...user,
|
|
5218
|
+
granularId,
|
|
5219
|
+
userId,
|
|
5220
|
+
subjectId: granularId,
|
|
5221
|
+
identityId: user.identityId || userId,
|
|
5222
|
+
permissions: Array.isArray(user.permissions) ? user.permissions : []
|
|
5223
|
+
};
|
|
5224
|
+
}
|
|
5199
5225
|
var Environment = class _Environment extends Session {
|
|
5200
5226
|
envData;
|
|
5201
5227
|
_apiKey;
|
|
@@ -5218,6 +5244,10 @@ var Environment = class _Environment extends Session {
|
|
|
5218
5244
|
get subjectId() {
|
|
5219
5245
|
return this.envData.subjectId;
|
|
5220
5246
|
}
|
|
5247
|
+
/** Internal Granular user identifier for this environment */
|
|
5248
|
+
get granularId() {
|
|
5249
|
+
return this.envData.subjectId;
|
|
5250
|
+
}
|
|
5221
5251
|
/** The permission profile ID */
|
|
5222
5252
|
get permissionProfileId() {
|
|
5223
5253
|
return this.envData.permissionProfileId;
|
|
@@ -6015,7 +6045,7 @@ var Granular = class {
|
|
|
6015
6045
|
* Records/upserts a user and prepares them for sandbox connections
|
|
6016
6046
|
*
|
|
6017
6047
|
* @param options - User options
|
|
6018
|
-
* @returns The user
|
|
6048
|
+
* @returns The recorded user with both `userId` and `granularId`
|
|
6019
6049
|
*
|
|
6020
6050
|
* @example
|
|
6021
6051
|
* ```typescript
|
|
@@ -6027,21 +6057,61 @@ var Granular = class {
|
|
|
6027
6057
|
* ```
|
|
6028
6058
|
*/
|
|
6029
6059
|
async recordUser(options) {
|
|
6030
|
-
const subject = await this.request("/control/subjects", {
|
|
6060
|
+
const subject = normalizeSubject(await this.request("/control/subjects", {
|
|
6031
6061
|
method: "POST",
|
|
6032
6062
|
body: JSON.stringify({
|
|
6033
6063
|
identityId: options.userId,
|
|
6034
6064
|
name: options.name,
|
|
6035
6065
|
email: options.email
|
|
6036
6066
|
})
|
|
6037
|
-
});
|
|
6038
|
-
return {
|
|
6067
|
+
}));
|
|
6068
|
+
return normalizeUser({
|
|
6069
|
+
granularId: subject.granularId,
|
|
6070
|
+
userId: options.userId,
|
|
6039
6071
|
subjectId: subject.subjectId,
|
|
6040
|
-
identityId:
|
|
6041
|
-
name: options.name,
|
|
6042
|
-
email: options.email,
|
|
6072
|
+
identityId: subject.identityId,
|
|
6073
|
+
name: options.name || subject.name || void 0,
|
|
6074
|
+
email: options.email || subject.email || void 0,
|
|
6043
6075
|
permissions: options.permissions || []
|
|
6044
|
-
};
|
|
6076
|
+
});
|
|
6077
|
+
}
|
|
6078
|
+
async resolveConnectUser(options) {
|
|
6079
|
+
if (options.user) {
|
|
6080
|
+
const user = normalizeUser(options.user);
|
|
6081
|
+
return {
|
|
6082
|
+
granularId: user.granularId,
|
|
6083
|
+
userId: user.userId,
|
|
6084
|
+
name: options.name || user.name,
|
|
6085
|
+
email: options.email || user.email,
|
|
6086
|
+
permissions: options.permissions || user.permissions
|
|
6087
|
+
};
|
|
6088
|
+
}
|
|
6089
|
+
if (options.userId) {
|
|
6090
|
+
const user = await this.recordUser({
|
|
6091
|
+
userId: options.userId,
|
|
6092
|
+
name: options.name,
|
|
6093
|
+
email: options.email,
|
|
6094
|
+
permissions: options.permissions
|
|
6095
|
+
});
|
|
6096
|
+
return {
|
|
6097
|
+
granularId: user.granularId,
|
|
6098
|
+
userId: user.userId,
|
|
6099
|
+
name: user.name,
|
|
6100
|
+
email: user.email,
|
|
6101
|
+
permissions: options.permissions || user.permissions
|
|
6102
|
+
};
|
|
6103
|
+
}
|
|
6104
|
+
if (options.granularId) {
|
|
6105
|
+
const subject = await this.subjects.get(options.granularId);
|
|
6106
|
+
return {
|
|
6107
|
+
granularId: subject.granularId,
|
|
6108
|
+
userId: subject.userId,
|
|
6109
|
+
name: options.name || subject.name || void 0,
|
|
6110
|
+
email: options.email || subject.email || void 0,
|
|
6111
|
+
permissions: options.permissions || []
|
|
6112
|
+
};
|
|
6113
|
+
}
|
|
6114
|
+
throw new Error("connect() requires either userId, granularId, or a user object returned by recordUser().");
|
|
6045
6115
|
}
|
|
6046
6116
|
/**
|
|
6047
6117
|
* Connect to a sandbox and establish a real-time environment session.
|
|
@@ -6055,14 +6125,10 @@ var Granular = class {
|
|
|
6055
6125
|
*
|
|
6056
6126
|
* @example
|
|
6057
6127
|
* ```typescript
|
|
6058
|
-
* const user = await granular.recordUser({
|
|
6059
|
-
* userId: 'user_123',
|
|
6060
|
-
* permissions: ['agent'],
|
|
6061
|
-
* });
|
|
6062
|
-
*
|
|
6063
6128
|
* const environment = await granular.connect({
|
|
6064
6129
|
* sandbox: 'my-sandbox',
|
|
6065
|
-
*
|
|
6130
|
+
* userId: 'user_123',
|
|
6131
|
+
* permissions: ['agent'],
|
|
6066
6132
|
* });
|
|
6067
6133
|
*
|
|
6068
6134
|
* await granular.registerEffect('my-sandbox', {
|
|
@@ -6083,13 +6149,14 @@ var Granular = class {
|
|
|
6083
6149
|
*/
|
|
6084
6150
|
async connect(options) {
|
|
6085
6151
|
const clientId = options.clientId || `client_${Date.now()}`;
|
|
6152
|
+
const user = await this.resolveConnectUser(options);
|
|
6086
6153
|
const sandbox = await this.findOrCreateSandbox(options.sandbox);
|
|
6087
|
-
for (const profileName of
|
|
6154
|
+
for (const profileName of user.permissions) {
|
|
6088
6155
|
const profileId = await this.ensurePermissionProfile(sandbox.sandboxId, profileName);
|
|
6089
|
-
await this.ensureAssignment(
|
|
6156
|
+
await this.ensureAssignment(user.granularId, sandbox.sandboxId, profileId);
|
|
6090
6157
|
}
|
|
6091
6158
|
const envData = await this.environments.create(sandbox.sandboxId, {
|
|
6092
|
-
subjectId:
|
|
6159
|
+
subjectId: user.granularId,
|
|
6093
6160
|
permissionProfileId: null
|
|
6094
6161
|
});
|
|
6095
6162
|
await this.activateEnvironment(envData.environmentId);
|
|
@@ -6149,7 +6216,21 @@ var Granular = class {
|
|
|
6149
6216
|
const effects = Array.from(this.getSandboxEffectMap(host.sandboxId).values()).map(
|
|
6150
6217
|
(effect) => this.serializeEffect(effect)
|
|
6151
6218
|
);
|
|
6152
|
-
await host.wsClient.call("effects.publishCatalog", { effects });
|
|
6219
|
+
const result = await host.wsClient.call("effects.publishCatalog", { effects });
|
|
6220
|
+
const acceptedCount = typeof result?.acceptedCount === "number" ? result.acceptedCount : 0;
|
|
6221
|
+
const rejected = Array.isArray(result?.rejected) ? result.rejected : [];
|
|
6222
|
+
if (acceptedCount === 0 && rejected.length > 0) {
|
|
6223
|
+
const detail = rejected.map((entry) => `${entry.name || "unknown"}: ${entry.reason || "rejected"}`).join("; ");
|
|
6224
|
+
throw new Error(
|
|
6225
|
+
`Failed to publish live effects for sandbox ${host.sandboxId}: ${detail}`
|
|
6226
|
+
);
|
|
6227
|
+
}
|
|
6228
|
+
if (rejected.length > 0) {
|
|
6229
|
+
console.warn(
|
|
6230
|
+
`[Granular] Some live effects were rejected for sandbox ${host.sandboxId}:`,
|
|
6231
|
+
rejected
|
|
6232
|
+
);
|
|
6233
|
+
}
|
|
6153
6234
|
}
|
|
6154
6235
|
async syncSandboxEffectCatalog(sandboxId) {
|
|
6155
6236
|
const host = await this.ensureSandboxEffectHost(sandboxId);
|
|
@@ -6315,6 +6396,22 @@ var Granular = class {
|
|
|
6315
6396
|
}
|
|
6316
6397
|
await this.syncSandboxEffectCatalog(sandboxId);
|
|
6317
6398
|
}
|
|
6399
|
+
/**
|
|
6400
|
+
* Disconnect one sandbox-scoped effect host, or all of them when no sandbox is provided.
|
|
6401
|
+
*
|
|
6402
|
+
* This is primarily useful for long-lived helper processes such as generated
|
|
6403
|
+
* `granular-effects.ts` scripts that need to shut down cleanly on SIGINT/SIGTERM.
|
|
6404
|
+
*/
|
|
6405
|
+
async disconnectEffects(sandboxNameOrId) {
|
|
6406
|
+
if (sandboxNameOrId) {
|
|
6407
|
+
const sandbox = await this.findOrCreateSandbox(sandboxNameOrId);
|
|
6408
|
+
this.disconnectSandboxEffectHost(sandbox.sandboxId);
|
|
6409
|
+
return;
|
|
6410
|
+
}
|
|
6411
|
+
for (const sandboxId of Array.from(this.sandboxEffectHosts.keys())) {
|
|
6412
|
+
this.disconnectSandboxEffectHost(sandboxId);
|
|
6413
|
+
}
|
|
6414
|
+
}
|
|
6318
6415
|
/**
|
|
6319
6416
|
* Unregister all effects for a sandbox.
|
|
6320
6417
|
*/
|
|
@@ -6492,7 +6589,7 @@ var Granular = class {
|
|
|
6492
6589
|
get subjects() {
|
|
6493
6590
|
return {
|
|
6494
6591
|
get: async (subjectId) => {
|
|
6495
|
-
return this.request(`/control/subjects/${subjectId}`);
|
|
6592
|
+
return normalizeSubject(await this.request(`/control/subjects/${subjectId}`));
|
|
6496
6593
|
},
|
|
6497
6594
|
listAssignments: async (subjectId) => {
|
|
6498
6595
|
return this.request(`/control/subjects/${subjectId}/assignments`);
|
|
@@ -6505,17 +6602,17 @@ var Granular = class {
|
|
|
6505
6602
|
get users() {
|
|
6506
6603
|
return {
|
|
6507
6604
|
create: async (data) => {
|
|
6508
|
-
return this.request("/control/subjects", {
|
|
6605
|
+
return normalizeSubject(await this.request("/control/subjects", {
|
|
6509
6606
|
method: "POST",
|
|
6510
6607
|
body: JSON.stringify({
|
|
6511
6608
|
identityId: data.id,
|
|
6512
6609
|
name: data.name,
|
|
6513
6610
|
email: data.email
|
|
6514
6611
|
})
|
|
6515
|
-
});
|
|
6612
|
+
}));
|
|
6516
6613
|
},
|
|
6517
6614
|
get: async (id) => {
|
|
6518
|
-
return this.request(`/control/subjects/${id}`);
|
|
6615
|
+
return normalizeSubject(await this.request(`/control/subjects/${id}`));
|
|
6519
6616
|
}
|
|
6520
6617
|
};
|
|
6521
6618
|
}
|