@granular-software/sdk 0.4.51 → 0.4.52
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/dist/agent-evals.d.mts +2 -2
- package/dist/agent-evals.d.ts +2 -2
- package/dist/agent-evals.js +236 -31
- package/dist/agent-evals.js.map +1 -1
- package/dist/agent-evals.mjs +236 -31
- package/dist/agent-evals.mjs.map +1 -1
- package/dist/cli/index.js +267 -50
- package/dist/{client-djorlOpn.d.mts → client-B2OyGOHE.d.mts} +18 -3
- package/dist/{client-NSH-tpNU.d.ts → client-BMfjRJTs.d.ts} +18 -3
- package/dist/index.d.mts +3 -3
- package/dist/index.d.ts +3 -3
- package/dist/index.js +236 -31
- package/dist/index.js.map +1 -1
- package/dist/index.mjs +236 -31
- package/dist/index.mjs.map +1 -1
- package/dist/{spend-BA-jZwZ0.d.mts → spend-DpRRCrAr.d.mts} +37 -2
- package/dist/{spend-BA-jZwZ0.d.ts → spend-DpRRCrAr.d.ts} +37 -2
- package/dist/spend.d.mts +1 -1
- package/dist/spend.d.ts +1 -1
- package/package.json +1 -1
package/dist/index.mjs
CHANGED
|
@@ -14287,7 +14287,8 @@ function normalizeEnvironmentSetupSummary(setup) {
|
|
|
14287
14287
|
environmentId: String(setup.environmentId || ""),
|
|
14288
14288
|
sandboxId: String(setup.sandboxId || ""),
|
|
14289
14289
|
subjectId: String(setup.subjectId || ""),
|
|
14290
|
-
triggerReason: setup.triggerReason === "fresh_after_version_update" ? "fresh_after_version_update" : "new_environment",
|
|
14290
|
+
triggerReason: setup.triggerReason === "fresh_after_version_update" ? "fresh_after_version_update" : setup.triggerReason === "explicit_reset" ? "explicit_reset" : "new_environment",
|
|
14291
|
+
operationKey: typeof setup.operationKey === "string" ? setup.operationKey : null,
|
|
14291
14292
|
lifecycleStatus: setup.lifecycleStatus === "completed" || setup.lifecycleStatus === "failed" ? setup.lifecycleStatus : "running",
|
|
14292
14293
|
stage: typeof setup.stage === "string" ? setup.stage : null,
|
|
14293
14294
|
totalObjectsToImport: Number(setup.totalObjectsToImport || 0),
|
|
@@ -15569,6 +15570,7 @@ var Environment = class _Environment {
|
|
|
15569
15570
|
records: recordsToImport,
|
|
15570
15571
|
batchSize: options.batchSize,
|
|
15571
15572
|
setupRunId: options.setupRunId,
|
|
15573
|
+
operationKey: options.operationKey,
|
|
15572
15574
|
writeMode: options.writeMode
|
|
15573
15575
|
})
|
|
15574
15576
|
}
|
|
@@ -16373,6 +16375,107 @@ var Granular = class _Granular {
|
|
|
16373
16375
|
await this.maybeRunEnvironmentImporter(resolved, environment);
|
|
16374
16376
|
return environment;
|
|
16375
16377
|
}
|
|
16378
|
+
/**
|
|
16379
|
+
* Read the active environment selected by Granular for an already-recorded
|
|
16380
|
+
* external user. This is intentionally read-only: browser/login code must
|
|
16381
|
+
* not create subjects or environments as a side effect.
|
|
16382
|
+
*/
|
|
16383
|
+
async getActiveEnvironmentForUser(options) {
|
|
16384
|
+
const sandboxId = options.sandboxId.trim();
|
|
16385
|
+
const tagName = options.tag.trim();
|
|
16386
|
+
const userId = options.userId.trim();
|
|
16387
|
+
if (!sandboxId || !tagName || !userId) {
|
|
16388
|
+
throw new Error(
|
|
16389
|
+
"getActiveEnvironmentForUser() requires sandboxId, tag, and userId."
|
|
16390
|
+
);
|
|
16391
|
+
}
|
|
16392
|
+
const subjects = await this.request(
|
|
16393
|
+
`/control/subjects?identityId=${encodeURIComponent(userId)}`
|
|
16394
|
+
);
|
|
16395
|
+
const subject = (subjects.items || []).find(
|
|
16396
|
+
(item) => item.identityId === userId || item.userId === userId
|
|
16397
|
+
);
|
|
16398
|
+
if (!subject?.subjectId && !subject?.granularId) {
|
|
16399
|
+
return null;
|
|
16400
|
+
}
|
|
16401
|
+
const subjectId = subject.subjectId || subject.granularId;
|
|
16402
|
+
const tags = await this.request(
|
|
16403
|
+
`/control/sandboxes/${encodeURIComponent(sandboxId)}/tags`
|
|
16404
|
+
);
|
|
16405
|
+
const tag = (tags.items || []).find(
|
|
16406
|
+
(item) => item?.name === tagName
|
|
16407
|
+
);
|
|
16408
|
+
if (!tag) return null;
|
|
16409
|
+
const query = new URLSearchParams({
|
|
16410
|
+
tagId: tag.tagId,
|
|
16411
|
+
slot: options.slot?.trim() || "default"
|
|
16412
|
+
});
|
|
16413
|
+
try {
|
|
16414
|
+
const payload = await this.request(
|
|
16415
|
+
`/control/sandboxes/${encodeURIComponent(sandboxId)}/subjects/${encodeURIComponent(subjectId)}/active-environment?${query.toString()}`
|
|
16416
|
+
);
|
|
16417
|
+
return payload.environment ? this.bindEnvironmentHandle(
|
|
16418
|
+
normalizeEnvironmentData(payload.environment)
|
|
16419
|
+
) : null;
|
|
16420
|
+
} catch (error) {
|
|
16421
|
+
const message = error instanceof Error ? error.message : String(error);
|
|
16422
|
+
if (message.includes("404") || message.includes("not found")) {
|
|
16423
|
+
return null;
|
|
16424
|
+
}
|
|
16425
|
+
throw error;
|
|
16426
|
+
}
|
|
16427
|
+
}
|
|
16428
|
+
/**
|
|
16429
|
+
* Register one reviewed, pre-existing environment as the active workspace
|
|
16430
|
+
* for an external user. This is for a controlled migration only: it does
|
|
16431
|
+
* not create an environment and it does not run an importer.
|
|
16432
|
+
*/
|
|
16433
|
+
async adoptEnvironmentForUser(options) {
|
|
16434
|
+
const sandboxId = options.sandboxId.trim();
|
|
16435
|
+
const tagName = options.tag.trim();
|
|
16436
|
+
const userId = options.userId.trim();
|
|
16437
|
+
const environmentId = options.environmentId.trim();
|
|
16438
|
+
if (!sandboxId || !tagName || !userId || !environmentId) {
|
|
16439
|
+
throw new Error(
|
|
16440
|
+
"adoptEnvironmentForUser() requires sandboxId, tag, userId, and environmentId."
|
|
16441
|
+
);
|
|
16442
|
+
}
|
|
16443
|
+
const subjects = await this.request(
|
|
16444
|
+
`/control/subjects?identityId=${encodeURIComponent(userId)}`
|
|
16445
|
+
);
|
|
16446
|
+
const subject = (subjects.items || []).find(
|
|
16447
|
+
(item) => item.identityId === userId || item.userId === userId
|
|
16448
|
+
);
|
|
16449
|
+
if (!subject?.subjectId && !subject?.granularId) {
|
|
16450
|
+
throw new Error(`No Granular subject exists for user ${userId}.`);
|
|
16451
|
+
}
|
|
16452
|
+
const tags = await this.request(`/control/sandboxes/${encodeURIComponent(sandboxId)}/tags`);
|
|
16453
|
+
const tag = (tags.items || []).find(
|
|
16454
|
+
(item) => item?.name === tagName
|
|
16455
|
+
);
|
|
16456
|
+
if (!tag) {
|
|
16457
|
+
throw new Error(`Tag ${tagName} was not found in sandbox ${sandboxId}.`);
|
|
16458
|
+
}
|
|
16459
|
+
const payload = await this.request(
|
|
16460
|
+
"/control/environment-activations/adopt",
|
|
16461
|
+
{
|
|
16462
|
+
method: "POST",
|
|
16463
|
+
body: JSON.stringify({
|
|
16464
|
+
environmentId,
|
|
16465
|
+
subjectId: subject.subjectId || subject.granularId,
|
|
16466
|
+
tagId: tag.tagId,
|
|
16467
|
+
slot: options.slot?.trim() || "default",
|
|
16468
|
+
confirmExistingData: true
|
|
16469
|
+
})
|
|
16470
|
+
}
|
|
16471
|
+
);
|
|
16472
|
+
if (!payload.environment) {
|
|
16473
|
+
throw new Error("Granular did not return the adopted environment.");
|
|
16474
|
+
}
|
|
16475
|
+
return this.bindEnvironmentHandle(
|
|
16476
|
+
normalizeEnvironmentData(payload.environment)
|
|
16477
|
+
);
|
|
16478
|
+
}
|
|
16376
16479
|
/**
|
|
16377
16480
|
* Deprecated compatibility alias for `openEnvironment()`.
|
|
16378
16481
|
*
|
|
@@ -16404,7 +16507,9 @@ var Granular = class _Granular {
|
|
|
16404
16507
|
requestedOntology,
|
|
16405
16508
|
sandboxId: environmentData.sandboxId,
|
|
16406
16509
|
subjectId: environmentData.subjectId,
|
|
16407
|
-
|
|
16510
|
+
externalUserId: environmentData.subjectId,
|
|
16511
|
+
setupTriggerReason: options.reason || "new_environment",
|
|
16512
|
+
setupOperationKey: options.operationKey
|
|
16408
16513
|
},
|
|
16409
16514
|
environment
|
|
16410
16515
|
);
|
|
@@ -16416,20 +16521,17 @@ var Granular = class _Granular {
|
|
|
16416
16521
|
}
|
|
16417
16522
|
return tag;
|
|
16418
16523
|
}
|
|
16419
|
-
buildManagedEnvironmentName(tag, versionId) {
|
|
16420
|
-
|
|
16524
|
+
buildManagedEnvironmentName(tag, versionId, resetKey) {
|
|
16525
|
+
if (!resetKey) {
|
|
16526
|
+
return `__sdk__${tag}__${versionId}__tracked`;
|
|
16527
|
+
}
|
|
16528
|
+
const safeResetKey = resetKey.replace(/[^a-zA-Z0-9_-]/g, "_").slice(0, 80);
|
|
16529
|
+
return `__sdk__${tag}__${versionId}__reset__${safeResetKey}`;
|
|
16421
16530
|
}
|
|
16422
16531
|
isManagedEnvironmentName(environment, tagName) {
|
|
16423
16532
|
const name = environment.environment || environment.envName || "";
|
|
16424
16533
|
return name.startsWith(`__sdk__${tagName}__`);
|
|
16425
16534
|
}
|
|
16426
|
-
isPinnedToVersion(environment, versionId) {
|
|
16427
|
-
return environment.buildPolicy.mode === "pinned" && (environment.versionId === versionId || environment.buildPolicy.versionId === versionId || environment.buildPolicy.buildId === versionId);
|
|
16428
|
-
}
|
|
16429
|
-
matchesTagTrackedEnvironment(environment, tagName, tagId) {
|
|
16430
|
-
const environmentTagName = environment.tag?.name || environment.buildPolicy.tagName || null;
|
|
16431
|
-
return environment.tagId === tagId || environmentTagName === tagName || environment.environment === tagName || environment.envName === tagName || environment.environment === this.buildManagedEnvironmentName(tagName, environment.versionId) || environment.envName === this.buildManagedEnvironmentName(tagName, environment.versionId);
|
|
16432
|
-
}
|
|
16433
16535
|
sortEnvironmentsByRecency(environments) {
|
|
16434
16536
|
return [...environments].sort(
|
|
16435
16537
|
(left, right) => right.updatedAt - left.updatedAt
|
|
@@ -16479,48 +16581,119 @@ var Granular = class _Granular {
|
|
|
16479
16581
|
`Tag "${tagName}" does not currently point to a build/version.`
|
|
16480
16582
|
);
|
|
16481
16583
|
}
|
|
16584
|
+
const slot = options.slot?.trim() || "default";
|
|
16585
|
+
const resetKey = options.resetKey?.trim() || void 0;
|
|
16586
|
+
const resolveActive = async (operationKey) => {
|
|
16587
|
+
const query = new URLSearchParams({ tagId: tag.tagId, slot });
|
|
16588
|
+
if (operationKey) query.set("operationKey", operationKey);
|
|
16589
|
+
try {
|
|
16590
|
+
const payload = await this.request(
|
|
16591
|
+
`/control/sandboxes/${encodeURIComponent(sandbox.sandboxId)}/subjects/${encodeURIComponent(user.granularId)}/active-environment?${query.toString()}`
|
|
16592
|
+
);
|
|
16593
|
+
return payload.environment ? normalizeEnvironmentData(payload.environment) : null;
|
|
16594
|
+
} catch (error) {
|
|
16595
|
+
const message = error instanceof Error ? error.message : String(error);
|
|
16596
|
+
if (message.includes("404") || message.includes("not found")) {
|
|
16597
|
+
return null;
|
|
16598
|
+
}
|
|
16599
|
+
throw error;
|
|
16600
|
+
}
|
|
16601
|
+
};
|
|
16602
|
+
const activate = async (environment2) => {
|
|
16603
|
+
const payload = await this.request(
|
|
16604
|
+
"/control/environment-activations",
|
|
16605
|
+
{
|
|
16606
|
+
method: "POST",
|
|
16607
|
+
body: JSON.stringify({
|
|
16608
|
+
environmentId: environment2.environmentId,
|
|
16609
|
+
tagId: tag.tagId,
|
|
16610
|
+
slot,
|
|
16611
|
+
operationKey: resetKey,
|
|
16612
|
+
operation: resetKey ? "explicit_reset" : void 0
|
|
16613
|
+
})
|
|
16614
|
+
}
|
|
16615
|
+
);
|
|
16616
|
+
return normalizeEnvironmentData(payload.environment);
|
|
16617
|
+
};
|
|
16618
|
+
if (resetKey) {
|
|
16619
|
+
const resetEnvironment = await resolveActive(resetKey);
|
|
16620
|
+
if (resetEnvironment) {
|
|
16621
|
+
return {
|
|
16622
|
+
environment: resetEnvironment,
|
|
16623
|
+
requestedOntology: ontology,
|
|
16624
|
+
sandboxId: sandbox.sandboxId,
|
|
16625
|
+
subjectId: user.granularId,
|
|
16626
|
+
externalUserId: user.userId,
|
|
16627
|
+
// Retrying an explicit reset must also resume its durable setup run.
|
|
16628
|
+
// Otherwise a Container crash after queue submission would leave a
|
|
16629
|
+
// valid environment permanently marked as "running".
|
|
16630
|
+
setupTriggerReason: "explicit_reset",
|
|
16631
|
+
setupOperationKey: resetKey
|
|
16632
|
+
};
|
|
16633
|
+
}
|
|
16634
|
+
} else {
|
|
16635
|
+
const active = await resolveActive();
|
|
16636
|
+
if (active && (active.versionId === targetVersionId || options.createFreshIfOutdated !== true)) {
|
|
16637
|
+
return {
|
|
16638
|
+
environment: active,
|
|
16639
|
+
requestedOntology: ontology,
|
|
16640
|
+
sandboxId: sandbox.sandboxId,
|
|
16641
|
+
subjectId: user.granularId,
|
|
16642
|
+
externalUserId: user.userId
|
|
16643
|
+
};
|
|
16644
|
+
}
|
|
16645
|
+
}
|
|
16482
16646
|
const allEnvironments = await this.environments.list(sandbox.sandboxId);
|
|
16483
16647
|
const userEnvironments = allEnvironments.filter(
|
|
16484
|
-
(
|
|
16648
|
+
(environment2) => environment2.subjectId === user.granularId
|
|
16485
16649
|
);
|
|
16486
16650
|
const currentMatches = this.sortEnvironmentsByRecency(
|
|
16487
16651
|
userEnvironments.filter(
|
|
16488
|
-
(
|
|
16652
|
+
(environment2) => environment2.tagId === tag.tagId && environment2.versionId === targetVersionId
|
|
16489
16653
|
)
|
|
16490
16654
|
);
|
|
16491
|
-
if (currentMatches.length > 0) {
|
|
16655
|
+
if (!resetKey && currentMatches.length > 0) {
|
|
16656
|
+
const environment2 = await activate(currentMatches[0]);
|
|
16492
16657
|
return {
|
|
16493
|
-
environment:
|
|
16658
|
+
environment: environment2,
|
|
16494
16659
|
requestedOntology: ontology,
|
|
16495
16660
|
sandboxId: sandbox.sandboxId,
|
|
16496
|
-
subjectId: user.granularId
|
|
16661
|
+
subjectId: user.granularId,
|
|
16662
|
+
externalUserId: user.userId
|
|
16497
16663
|
};
|
|
16498
16664
|
}
|
|
16499
16665
|
const outdatedMatches = this.sortEnvironmentsByRecency(
|
|
16500
|
-
userEnvironments.filter(
|
|
16501
|
-
(environment) => this.matchesTagTrackedEnvironment(environment, tagName, tag.tagId)
|
|
16502
|
-
)
|
|
16666
|
+
userEnvironments.filter((environment2) => environment2.tagId === tag.tagId)
|
|
16503
16667
|
);
|
|
16504
16668
|
if (outdatedMatches.length > 0 && options.createFreshIfOutdated !== true) {
|
|
16669
|
+
const environment2 = await activate(outdatedMatches[0]);
|
|
16505
16670
|
return {
|
|
16506
|
-
environment:
|
|
16671
|
+
environment: environment2,
|
|
16507
16672
|
requestedOntology: ontology,
|
|
16508
16673
|
sandboxId: sandbox.sandboxId,
|
|
16509
|
-
subjectId: user.granularId
|
|
16674
|
+
subjectId: user.granularId,
|
|
16675
|
+
externalUserId: user.userId
|
|
16510
16676
|
};
|
|
16511
16677
|
}
|
|
16678
|
+
const created = await this.environments.create(sandbox.sandboxId, {
|
|
16679
|
+
subjectId: user.granularId,
|
|
16680
|
+
environment: this.buildManagedEnvironmentName(
|
|
16681
|
+
tagName,
|
|
16682
|
+
targetVersionId,
|
|
16683
|
+
resetKey
|
|
16684
|
+
),
|
|
16685
|
+
tagId: tag.tagId,
|
|
16686
|
+
permissionProfileId: null
|
|
16687
|
+
});
|
|
16688
|
+
const environment = await activate(created);
|
|
16512
16689
|
return {
|
|
16513
|
-
environment
|
|
16514
|
-
subjectId: user.granularId,
|
|
16515
|
-
environment: this.buildManagedEnvironmentName(tagName, targetVersionId),
|
|
16516
|
-
tagId: tag.tagId,
|
|
16517
|
-
versionId: targetVersionId,
|
|
16518
|
-
permissionProfileId: null
|
|
16519
|
-
}),
|
|
16690
|
+
environment,
|
|
16520
16691
|
requestedOntology: ontology,
|
|
16521
16692
|
sandboxId: sandbox.sandboxId,
|
|
16522
16693
|
subjectId: user.granularId,
|
|
16523
|
-
|
|
16694
|
+
externalUserId: user.userId,
|
|
16695
|
+
setupTriggerReason: resetKey ? "explicit_reset" : outdatedMatches.length > 0 ? "fresh_after_version_update" : "new_environment",
|
|
16696
|
+
setupOperationKey: resetKey
|
|
16524
16697
|
};
|
|
16525
16698
|
}
|
|
16526
16699
|
/**
|
|
@@ -16799,11 +16972,24 @@ var Granular = class _Granular {
|
|
|
16799
16972
|
{
|
|
16800
16973
|
method: "POST",
|
|
16801
16974
|
body: JSON.stringify({
|
|
16802
|
-
triggerReason: resolved.setupTriggerReason
|
|
16975
|
+
triggerReason: resolved.setupTriggerReason,
|
|
16976
|
+
operationKey: resolved.setupOperationKey
|
|
16803
16977
|
})
|
|
16804
16978
|
}
|
|
16805
16979
|
);
|
|
16806
16980
|
const setupRunId = setupRun.setupRunId;
|
|
16981
|
+
let claim = null;
|
|
16982
|
+
for (let attempt = 0; attempt < 3; attempt += 1) {
|
|
16983
|
+
claim = await this.request(
|
|
16984
|
+
`/control/environment-setup-runs/${setupRunId}/importer-claim`,
|
|
16985
|
+
{ method: "POST", body: JSON.stringify({}) }
|
|
16986
|
+
);
|
|
16987
|
+
if (claim.action !== "busy") break;
|
|
16988
|
+
await sleep(Math.min(3e4, Math.max(250, claim.retryAfterMs || 1e3)));
|
|
16989
|
+
}
|
|
16990
|
+
if (!claim) {
|
|
16991
|
+
throw new Error(`Unable to claim environment setup run ${setupRunId}.`);
|
|
16992
|
+
}
|
|
16807
16993
|
const updateSetupRun = async (patch) => {
|
|
16808
16994
|
await this.request(
|
|
16809
16995
|
`/control/environment-setup-runs/${setupRunId}`,
|
|
@@ -16813,10 +16999,26 @@ var Granular = class _Granular {
|
|
|
16813
16999
|
}
|
|
16814
17000
|
);
|
|
16815
17001
|
};
|
|
17002
|
+
if (claim.action === "submitted") {
|
|
17003
|
+
const completedSetupRun = await this.request(
|
|
17004
|
+
`/control/environment-setup-runs/${setupRunId}`,
|
|
17005
|
+
{ method: "PATCH", body: JSON.stringify({ markHookCompleted: true }) }
|
|
17006
|
+
);
|
|
17007
|
+
const refreshedEnvironment = await this.environments.get(
|
|
17008
|
+
environment.environmentId
|
|
17009
|
+
);
|
|
17010
|
+
environment.syncEnvironmentData(refreshedEnvironment);
|
|
17011
|
+
return completedSetupRun;
|
|
17012
|
+
}
|
|
17013
|
+
if (claim.action === "busy" || claim.action === "terminal") {
|
|
17014
|
+
return claim.summary;
|
|
17015
|
+
}
|
|
17016
|
+
let importSequence = 0;
|
|
16816
17017
|
const importerContext = {
|
|
16817
17018
|
environmentId: environment.environmentId,
|
|
16818
17019
|
sandboxId: environment.sandboxId,
|
|
16819
17020
|
subjectId: environment.subjectId,
|
|
17021
|
+
externalUserId: resolved.externalUserId,
|
|
16820
17022
|
reason: resolved.setupTriggerReason,
|
|
16821
17023
|
incrementTotalObjectsToImportCount: async (n) => {
|
|
16822
17024
|
const safeIncrement = Math.max(0, Math.trunc(n));
|
|
@@ -16833,7 +17035,10 @@ var Granular = class _Granular {
|
|
|
16833
17035
|
importRecords: async (records, options) => environment.enqueueRecordImport(records, {
|
|
16834
17036
|
batchSize: options?.batchSize,
|
|
16835
17037
|
writeMode: options?.writeMode,
|
|
16836
|
-
setupRunId
|
|
17038
|
+
setupRunId,
|
|
17039
|
+
// Sequence is deterministic for a retry of one importer hook. It
|
|
17040
|
+
// prevents a Container restart from creating a second queue import.
|
|
17041
|
+
operationKey: `${setupRunId}:import:${importSequence++}`
|
|
16837
17042
|
})
|
|
16838
17043
|
};
|
|
16839
17044
|
try {
|