@granular-software/sdk 0.4.47 → 0.4.49
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 +36 -12
- package/dist/agent-evals.d.mts +2 -2
- package/dist/agent-evals.d.ts +2 -2
- package/dist/agent-evals.js +498 -208
- package/dist/agent-evals.js.map +1 -1
- package/dist/agent-evals.mjs +498 -208
- package/dist/agent-evals.mjs.map +1 -1
- package/dist/agent-harness.d.mts +1 -1
- package/dist/agent-harness.d.ts +1 -1
- package/dist/agent-harness.js +204 -142
- package/dist/agent-harness.js.map +1 -1
- package/dist/agent-harness.mjs +204 -142
- package/dist/agent-harness.mjs.map +1 -1
- package/dist/cli/index.js +547 -182
- package/dist/{client-C1UqPDwe.d.mts → client-B-MPVvDr.d.mts} +24 -3
- package/dist/{client-ButG6ePW.d.ts → client-zihxkDDs.d.ts} +24 -3
- package/dist/index.d.mts +3 -3
- package/dist/index.d.ts +3 -3
- package/dist/index.js +452 -194
- package/dist/index.js.map +1 -1
- package/dist/index.mjs +452 -194
- package/dist/index.mjs.map +1 -1
- package/dist/{spend-D2Vy3N1D.d.mts → spend-CStuOBXb.d.mts} +83 -3
- package/dist/{spend-D2Vy3N1D.d.ts → spend-CStuOBXb.d.ts} +83 -3
- package/dist/spend.d.mts +1 -1
- package/dist/spend.d.ts +1 -1
- package/package.json +1 -1
package/dist/cli/index.js
CHANGED
|
@@ -5434,7 +5434,9 @@ function resolveEndpointMode(explicitMode) {
|
|
|
5434
5434
|
if (explicit === "local" || explicit === "production") {
|
|
5435
5435
|
return explicit;
|
|
5436
5436
|
}
|
|
5437
|
-
const envMode = normalizeMode(
|
|
5437
|
+
const envMode = normalizeMode(
|
|
5438
|
+
readEnv("GRANULAR_ENDPOINT_MODE") || readEnv("GRANULAR_ENV")
|
|
5439
|
+
);
|
|
5438
5440
|
if (envMode === "local" || envMode === "production") {
|
|
5439
5441
|
return envMode;
|
|
5440
5442
|
}
|
|
@@ -7639,7 +7641,11 @@ function ensureBuiltinPermissionFiles() {
|
|
|
7639
7641
|
for (const profile of getBuiltinPermissionProfiles()) {
|
|
7640
7642
|
const filePath = path3__namespace.join(permissionsDir, `${profile.name}.json`);
|
|
7641
7643
|
if (!fs__namespace.existsSync(filePath)) {
|
|
7642
|
-
fs__namespace.writeFileSync(
|
|
7644
|
+
fs__namespace.writeFileSync(
|
|
7645
|
+
filePath,
|
|
7646
|
+
JSON.stringify(profile, null, 2) + "\n",
|
|
7647
|
+
"utf-8"
|
|
7648
|
+
);
|
|
7643
7649
|
}
|
|
7644
7650
|
}
|
|
7645
7651
|
}
|
|
@@ -7708,7 +7714,10 @@ function loadApiUrl() {
|
|
|
7708
7714
|
if (process.env.GRANULAR_API_URL) return process.env.GRANULAR_API_URL;
|
|
7709
7715
|
const modeOverride = process.env.GRANULAR_ENDPOINT_MODE;
|
|
7710
7716
|
if (modeOverride === "local" || modeOverride === "production" || modeOverride === "prod") {
|
|
7711
|
-
return resolveApiUrl(
|
|
7717
|
+
return resolveApiUrl(
|
|
7718
|
+
void 0,
|
|
7719
|
+
modeOverride === "prod" ? "production" : modeOverride
|
|
7720
|
+
);
|
|
7712
7721
|
}
|
|
7713
7722
|
const rc = readRcFile();
|
|
7714
7723
|
if (rc.apiUrl) return rc.apiUrl;
|
|
@@ -7797,9 +7806,9 @@ var ApiClient = class {
|
|
|
7797
7806
|
const response = await fetch(url, {
|
|
7798
7807
|
...options,
|
|
7799
7808
|
headers: {
|
|
7800
|
-
|
|
7809
|
+
Authorization: `Bearer ${this.apiKey}`,
|
|
7801
7810
|
"Content-Type": "application/json",
|
|
7802
|
-
|
|
7811
|
+
Connection: "close",
|
|
7803
7812
|
...options.headers
|
|
7804
7813
|
}
|
|
7805
7814
|
});
|
|
@@ -7908,10 +7917,13 @@ var ApiClient = class {
|
|
|
7908
7917
|
}));
|
|
7909
7918
|
}
|
|
7910
7919
|
async moveTag(tagId, targetBuildId) {
|
|
7911
|
-
const moved = await this.request(
|
|
7912
|
-
|
|
7913
|
-
|
|
7914
|
-
|
|
7920
|
+
const moved = await this.request(
|
|
7921
|
+
`/control/tags/${tagId}/move`,
|
|
7922
|
+
{
|
|
7923
|
+
method: "POST",
|
|
7924
|
+
body: JSON.stringify({ targetBuildId })
|
|
7925
|
+
}
|
|
7926
|
+
);
|
|
7915
7927
|
return {
|
|
7916
7928
|
...moved,
|
|
7917
7929
|
targetVersionId: moved.targetVersionId ?? moved.targetBuildId ?? null
|
|
@@ -7921,14 +7933,20 @@ var ApiClient = class {
|
|
|
7921
7933
|
return this.moveTag(tagId, targetVersionId);
|
|
7922
7934
|
}
|
|
7923
7935
|
async promoteToProd(buildId) {
|
|
7924
|
-
return this.request(
|
|
7925
|
-
|
|
7926
|
-
|
|
7936
|
+
return this.request(
|
|
7937
|
+
`/control/builds/${buildId}/promote`,
|
|
7938
|
+
{
|
|
7939
|
+
method: "POST"
|
|
7940
|
+
}
|
|
7941
|
+
);
|
|
7927
7942
|
}
|
|
7928
7943
|
async promoteVersionToProd(versionId) {
|
|
7929
|
-
return this.request(
|
|
7930
|
-
|
|
7931
|
-
|
|
7944
|
+
return this.request(
|
|
7945
|
+
`/control/versions/${versionId}/promote`,
|
|
7946
|
+
{
|
|
7947
|
+
method: "POST"
|
|
7948
|
+
}
|
|
7949
|
+
);
|
|
7932
7950
|
}
|
|
7933
7951
|
async getBuildDiff(buildId, againstBuildId) {
|
|
7934
7952
|
const query = againstBuildId ? `?against=${encodeURIComponent(againstBuildId)}` : "";
|
|
@@ -7969,10 +7987,14 @@ var ApiClient = class {
|
|
|
7969
7987
|
schemaVersion: 1,
|
|
7970
7988
|
name: data.name
|
|
7971
7989
|
};
|
|
7972
|
-
const result = await this.syncPermissionProfileSources(sandboxId, [
|
|
7990
|
+
const result = await this.syncPermissionProfileSources(sandboxId, [
|
|
7991
|
+
profile
|
|
7992
|
+
]);
|
|
7973
7993
|
const synced = result.items?.find((item) => item.name === data.name) || result.items?.[0];
|
|
7974
7994
|
if (!synced) {
|
|
7975
|
-
throw new Error(
|
|
7995
|
+
throw new Error(
|
|
7996
|
+
`Permission profile source sync did not return ${data.name}`
|
|
7997
|
+
);
|
|
7976
7998
|
}
|
|
7977
7999
|
return synced;
|
|
7978
8000
|
}
|
|
@@ -7989,22 +8011,19 @@ var ApiClient = class {
|
|
|
7989
8011
|
});
|
|
7990
8012
|
}
|
|
7991
8013
|
async syncPermissionProfileSources(sandboxId, profiles) {
|
|
7992
|
-
return this.request(
|
|
7993
|
-
|
|
7994
|
-
{
|
|
7995
|
-
|
|
7996
|
-
body: JSON.stringify({ profiles })
|
|
7997
|
-
}
|
|
7998
|
-
);
|
|
8014
|
+
return this.request(`/control/sandboxes/${sandboxId}/permission-profile-sources`, {
|
|
8015
|
+
method: "PUT",
|
|
8016
|
+
body: JSON.stringify({ profiles })
|
|
8017
|
+
});
|
|
7999
8018
|
}
|
|
8000
8019
|
async getVersionPolicies(versionId) {
|
|
8001
|
-
return this.request(`/control/ontology-versions/${versionId}/policies`);
|
|
8002
|
-
}
|
|
8003
|
-
async getVersionPermissionProfiles(versionId) {
|
|
8004
8020
|
return this.request(
|
|
8005
|
-
`/control/ontology-versions/${versionId}/
|
|
8021
|
+
`/control/ontology-versions/${versionId}/policies`
|
|
8006
8022
|
);
|
|
8007
8023
|
}
|
|
8024
|
+
async getVersionPermissionProfiles(versionId) {
|
|
8025
|
+
return this.request(`/control/ontology-versions/${versionId}/permission-profiles`);
|
|
8026
|
+
}
|
|
8008
8027
|
async evaluateVersionPolicy(versionId, request) {
|
|
8009
8028
|
return this.request(`/control/builds/${versionId}/policies/evaluate`, {
|
|
8010
8029
|
method: "POST",
|
|
@@ -8094,7 +8113,9 @@ function generateHilbertPath(order = 4, size = 128, radius = 51) {
|
|
|
8094
8113
|
const [sqX, sqY] = squareToSquircle(normX, normY);
|
|
8095
8114
|
points.push([center + sqX * radius, center + sqY * radius]);
|
|
8096
8115
|
}
|
|
8097
|
-
return points.map(
|
|
8116
|
+
return points.map(
|
|
8117
|
+
([x, y], i) => `${i === 0 ? "M" : "L"} ${x.toFixed(3)} ${y.toFixed(3)}`
|
|
8118
|
+
).join(" ");
|
|
8098
8119
|
}
|
|
8099
8120
|
function renderLogoSvg() {
|
|
8100
8121
|
const squirclePath = generateSquirclePath();
|
|
@@ -8294,7 +8315,11 @@ async function loginWithBrowser(options) {
|
|
|
8294
8315
|
const timeout = setTimeout(() => {
|
|
8295
8316
|
if (settled) return;
|
|
8296
8317
|
settled = true;
|
|
8297
|
-
reject(
|
|
8318
|
+
reject(
|
|
8319
|
+
new Error(
|
|
8320
|
+
`Timed out after ${Math.round(timeoutMs / 1e3)}s waiting for browser authentication.`
|
|
8321
|
+
)
|
|
8322
|
+
);
|
|
8298
8323
|
}, timeoutMs);
|
|
8299
8324
|
server.on("request", (req, res) => {
|
|
8300
8325
|
const url = new URL(req.url || "/", `http://${CALLBACK_HOST}`);
|
|
@@ -8309,7 +8334,13 @@ async function loginWithBrowser(options) {
|
|
|
8309
8334
|
if (!returnedState || returnedState !== state) {
|
|
8310
8335
|
res.statusCode = 400;
|
|
8311
8336
|
res.setHeader("Content-Type", "text/html; charset=utf-8");
|
|
8312
|
-
res.end(
|
|
8337
|
+
res.end(
|
|
8338
|
+
renderHtmlPage(
|
|
8339
|
+
"Granular login failed",
|
|
8340
|
+
"State mismatch. Please close this window and retry.",
|
|
8341
|
+
"error"
|
|
8342
|
+
)
|
|
8343
|
+
);
|
|
8313
8344
|
if (!settled) {
|
|
8314
8345
|
settled = true;
|
|
8315
8346
|
clearTimeout(timeout);
|
|
@@ -8337,7 +8368,13 @@ async function loginWithBrowser(options) {
|
|
|
8337
8368
|
if (!apiKey2) {
|
|
8338
8369
|
res.statusCode = 400;
|
|
8339
8370
|
res.setHeader("Content-Type", "text/html; charset=utf-8");
|
|
8340
|
-
res.end(
|
|
8371
|
+
res.end(
|
|
8372
|
+
renderHtmlPage(
|
|
8373
|
+
"Granular login failed",
|
|
8374
|
+
"No API key was returned. Please retry.",
|
|
8375
|
+
"error"
|
|
8376
|
+
)
|
|
8377
|
+
);
|
|
8341
8378
|
if (!settled) {
|
|
8342
8379
|
settled = true;
|
|
8343
8380
|
clearTimeout(timeout);
|
|
@@ -8347,7 +8384,13 @@ async function loginWithBrowser(options) {
|
|
|
8347
8384
|
}
|
|
8348
8385
|
res.statusCode = 200;
|
|
8349
8386
|
res.setHeader("Content-Type", "text/html; charset=utf-8");
|
|
8350
|
-
res.end(
|
|
8387
|
+
res.end(
|
|
8388
|
+
renderHtmlPage(
|
|
8389
|
+
"Granular login complete",
|
|
8390
|
+
"Authentication succeeded. You can close this window.",
|
|
8391
|
+
"success"
|
|
8392
|
+
)
|
|
8393
|
+
);
|
|
8351
8394
|
if (!settled) {
|
|
8352
8395
|
settled = true;
|
|
8353
8396
|
clearTimeout(timeout);
|
|
@@ -9741,7 +9784,9 @@ function table(headers, rows) {
|
|
|
9741
9784
|
}
|
|
9742
9785
|
}
|
|
9743
9786
|
function hint(command, description) {
|
|
9744
|
-
console.log(
|
|
9787
|
+
console.log(
|
|
9788
|
+
` ${brand.muted("$")} ${brand.secondary(command)} ${brand.muted(description)}`
|
|
9789
|
+
);
|
|
9745
9790
|
}
|
|
9746
9791
|
function nextSteps(steps) {
|
|
9747
9792
|
console.log();
|
|
@@ -9875,7 +9920,7 @@ The rest of this guide assumes these terms.
|
|
|
9875
9920
|
| **Build run** | The CI-style compilation process. \`granular build\` creates or reuses the ontology version, then runs a build for it. |
|
|
9876
9921
|
| **Environment** | Return value of \`granular.openEnvironment({ ontology, tag, userId, \u2026 })\`. It is your **server-side** handle for \`recordObject\`, \`recordObjects\`, \`graphql\`, and \`sessions.create()\`. |
|
|
9877
9922
|
| **Session** | Live runtime connection opened from an environment with \`environment.sessions.create()\` or \`environment.sessions.connect()\`. Job, prompt, and streaming APIs live here. |
|
|
9878
|
-
| **Job** | Code string passed to \`session.submitJob(code)\`. Runs in Granular\u2019s **sandbox runtime** with access to
|
|
9923
|
+
| **Job** | Code string passed to \`session.submitJob(code)\`. Runs in Granular\u2019s **sandbox runtime** with access to Harness v3 imports such as \`@granular/domain/<Class>\` and \`@granular/actions/backend\`. |
|
|
9879
9924
|
| **Effect** | Declared with \`withEffect\`; **handler** registered in **your** process with \`granular.ontology(sandboxId).effects.registerMany(...)\`. Jobs invoke effects; handlers do IO outside Granular. |
|
|
9880
9925
|
| **Class** | Entity **kind** in the domain (e.g. \`book\`) with \`has\` fields in the manifest. |
|
|
9881
9926
|
| **Record / object** | One **instance**: \`className\`, \`id\`, \`fields\`, \`relationships\`. Use \`recordObject\` for one upsert, \`recordObjects\` for several immediate upserts, and record-import APIs for async bulk loads. |
|
|
@@ -9900,7 +9945,7 @@ function manifestGuideEndToEndSection() {
|
|
|
9900
9945
|
| 5 | \`recordObject\` / \`recordObjects\` | **Records** stored with correct fields and relationship keys. Use \`recordObject\` for one targeted write. Use \`recordObjects\` for one synchronous multi-record write with chunking (default 100 rows/request), retries, and optional \`onChunkComplete\` / \`concurrency\` progress. For async bulk loads use \`enqueueRecordImport\` + import status APIs. |
|
|
9901
9946
|
| 6 | \`const session = await environment.sessions.create();\` then \`session.submitJob(\`\u2026\`)\` | **Jobs** run; may call **effects** \u2192 your handlers execute and return results |
|
|
9902
9947
|
|
|
9903
|
-
**Accuracy tip:** After changing the manifest, always **re-build** before assuming generated
|
|
9948
|
+
**Accuracy tip:** After changing the manifest, always **re-build** before assuming generated Harness v3 imports, generated types, or effect signatures match the file on disk.`;
|
|
9904
9949
|
}
|
|
9905
9950
|
function sandboxDocMainConceptsSection() {
|
|
9906
9951
|
return `## Main concepts (recap)
|
|
@@ -9914,7 +9959,7 @@ function sandboxDocMainConceptsSection() {
|
|
|
9914
9959
|
| **Build run** | Compilation run that validates and materializes a version. |
|
|
9915
9960
|
| **Environment** | \`openEnvironment()\` result \u2014 **record**, **recordObjects**, **graphql**, and **session lifecycle** for this sandbox. |
|
|
9916
9961
|
| **Session** | Live runtime connection created from an environment; job and prompt APIs live here. |
|
|
9917
|
-
| **Job** | \`session.submitJob(...)\` code using
|
|
9962
|
+
| **Job** | \`session.submitJob(...)\` code using Harness v3 runtime imports. |
|
|
9918
9963
|
| **Effect** | Declared in manifest; **handler** in your process. |
|
|
9919
9964
|
|
|
9920
9965
|
**Full product + manifest how-to:** [docs/granular-manifest.md](docs/granular-manifest.md).`;
|
|
@@ -14126,6 +14171,9 @@ external_exports.object({
|
|
|
14126
14171
|
mode: external_exports.string().optional()
|
|
14127
14172
|
}).strict()
|
|
14128
14173
|
]).optional(),
|
|
14174
|
+
access: external_exports.enum(["read", "write", "ui"]).optional(),
|
|
14175
|
+
effectKind: external_exports.enum(["read", "write", "ui"]).optional(),
|
|
14176
|
+
sideEffect: external_exports.enum(["read", "write", "ui", "readonly", "read_only"]).optional(),
|
|
14129
14177
|
policies: PoliciesSchema.optional()
|
|
14130
14178
|
}).strict();
|
|
14131
14179
|
|
|
@@ -14585,7 +14633,12 @@ var effectBehaviorsMetamodelPackage = defineMetamodelPackage({
|
|
|
14585
14633
|
description
|
|
14586
14634
|
})
|
|
14587
14635
|
);
|
|
14588
|
-
return {
|
|
14636
|
+
return {
|
|
14637
|
+
model,
|
|
14638
|
+
kind: "dry_run",
|
|
14639
|
+
enabled: finalEnabled,
|
|
14640
|
+
description
|
|
14641
|
+
};
|
|
14589
14642
|
},
|
|
14590
14643
|
set_reverse: async (ant, { handler, description }) => {
|
|
14591
14644
|
const model = await run(
|
|
@@ -14631,7 +14684,10 @@ var effectBehaviorsMetamodelPackage = defineMetamodelPackage({
|
|
|
14631
14684
|
applyToMethodIR(methodIR, methodSummary) {
|
|
14632
14685
|
return {
|
|
14633
14686
|
...methodIR,
|
|
14634
|
-
docs: [
|
|
14687
|
+
docs: [
|
|
14688
|
+
...methodIR.docs,
|
|
14689
|
+
...buildEffectBehaviorDocs(methodSummary.effectBehaviors)
|
|
14690
|
+
]
|
|
14635
14691
|
};
|
|
14636
14692
|
}
|
|
14637
14693
|
}
|
|
@@ -14895,7 +14951,9 @@ var filterByMetamodelPackage = defineMetamodelPackage({
|
|
|
14895
14951
|
|
|
14896
14952
|
// ../metamodel-note/src/index.ts
|
|
14897
14953
|
function noteTexts(values) {
|
|
14898
|
-
return (values || []).map((item) => item?.text).filter(
|
|
14954
|
+
return (values || []).map((item) => item?.text).filter(
|
|
14955
|
+
(value) => typeof value === "string" && value.length > 0
|
|
14956
|
+
);
|
|
14899
14957
|
}
|
|
14900
14958
|
function buildNoteMutations(targetPath, notes) {
|
|
14901
14959
|
return normalizeNotesInput(notes).map((note) => ({
|
|
@@ -14925,7 +14983,10 @@ var noteMetamodelPackage = defineMetamodelPackage({
|
|
|
14925
14983
|
id: "note",
|
|
14926
14984
|
docs: {
|
|
14927
14985
|
fieldRows: [
|
|
14928
|
-
{
|
|
14986
|
+
{
|
|
14987
|
+
key: "note",
|
|
14988
|
+
description: "Advisory text attached to a field. Accepts a string or string array."
|
|
14989
|
+
}
|
|
14929
14990
|
],
|
|
14930
14991
|
modelRows: [
|
|
14931
14992
|
{ key: "note", description: "Advisory text on the class/model itself." }
|
|
@@ -15065,7 +15126,9 @@ function buildRequiredFieldMutations(fieldPath, required) {
|
|
|
15065
15126
|
var requiredMetamodelPackage = defineMetamodelPackage({
|
|
15066
15127
|
id: "required",
|
|
15067
15128
|
docs: {
|
|
15068
|
-
fieldRows: [
|
|
15129
|
+
fieldRows: [
|
|
15130
|
+
{ key: "required", description: "Marks the field as required." }
|
|
15131
|
+
]
|
|
15069
15132
|
},
|
|
15070
15133
|
graphql: {
|
|
15071
15134
|
typeDefs: [
|
|
@@ -15123,7 +15186,10 @@ var requiredMetamodelPackage = defineMetamodelPackage({
|
|
|
15123
15186
|
if (!propertySummary.required) return propertyIR;
|
|
15124
15187
|
return {
|
|
15125
15188
|
...propertyIR,
|
|
15126
|
-
docs: [
|
|
15189
|
+
docs: [
|
|
15190
|
+
...propertyIR.docs,
|
|
15191
|
+
propertySummary.required.message || "Required."
|
|
15192
|
+
]
|
|
15127
15193
|
};
|
|
15128
15194
|
}
|
|
15129
15195
|
}
|
|
@@ -15274,7 +15340,10 @@ function normalizeStateDefinitions(machine) {
|
|
|
15274
15340
|
const states = /* @__PURE__ */ new Map();
|
|
15275
15341
|
for (const rawState of machine.states || []) {
|
|
15276
15342
|
if (typeof rawState === "string") {
|
|
15277
|
-
states.set(rawState, {
|
|
15343
|
+
states.set(rawState, {
|
|
15344
|
+
name: rawState,
|
|
15345
|
+
isFinal: finalStates.has(rawState)
|
|
15346
|
+
});
|
|
15278
15347
|
continue;
|
|
15279
15348
|
}
|
|
15280
15349
|
states.set(rawState.name, {
|
|
@@ -15362,7 +15431,9 @@ function buildMachineMethods(classSummary, machine) {
|
|
|
15362
15431
|
},
|
|
15363
15432
|
{
|
|
15364
15433
|
name: `reach_${machine.name}`,
|
|
15365
|
-
docs: [
|
|
15434
|
+
docs: [
|
|
15435
|
+
`Reach a ${docsPrefix} state through the shortest allowed transition path.`
|
|
15436
|
+
],
|
|
15366
15437
|
static: false,
|
|
15367
15438
|
params: [{ name: "target", type: stateName }],
|
|
15368
15439
|
returnType: `Promise<${toPascalCase2(classSummary.name)}>`,
|
|
@@ -15422,7 +15493,9 @@ function buildMachineMethods(classSummary, machine) {
|
|
|
15422
15493
|
},
|
|
15423
15494
|
{
|
|
15424
15495
|
name: `paths_to_${machine.name}`,
|
|
15425
|
-
docs: [
|
|
15496
|
+
docs: [
|
|
15497
|
+
`List shortest transition paths from the current ${docsPrefix} state to a target state.`
|
|
15498
|
+
],
|
|
15426
15499
|
static: false,
|
|
15427
15500
|
params: [{ name: "target", type: stateName }],
|
|
15428
15501
|
returnType: `Promise<Array<{ states: ${stateName}[]; transitions: ${transitionName}[] }>>`,
|
|
@@ -15555,22 +15628,39 @@ var stateMachineMetamodelPackage = defineMetamodelPackage({
|
|
|
15555
15628
|
name: (value) => value.name,
|
|
15556
15629
|
state_machine: async (value) => await run(value.target.state_machine(value.name)),
|
|
15557
15630
|
add_state: async (value, { name, is_final }) => {
|
|
15558
|
-
await run(
|
|
15631
|
+
await run(
|
|
15632
|
+
value.target.add_state_machine_state(
|
|
15633
|
+
value.name,
|
|
15634
|
+
name,
|
|
15635
|
+
is_final ?? false
|
|
15636
|
+
)
|
|
15637
|
+
);
|
|
15559
15638
|
return value;
|
|
15560
15639
|
},
|
|
15561
15640
|
add_transition: async (value, { name, from, to }) => {
|
|
15562
|
-
await run(
|
|
15641
|
+
await run(
|
|
15642
|
+
value.target.add_state_machine_transition(
|
|
15643
|
+
value.name,
|
|
15644
|
+
name,
|
|
15645
|
+
from,
|
|
15646
|
+
to
|
|
15647
|
+
)
|
|
15648
|
+
);
|
|
15563
15649
|
return value;
|
|
15564
15650
|
},
|
|
15565
15651
|
activate_transition: async (value, { name }) => {
|
|
15566
|
-
await run(
|
|
15652
|
+
await run(
|
|
15653
|
+
value.target.activate_state_machine_transition(value.name, name)
|
|
15654
|
+
);
|
|
15567
15655
|
return value;
|
|
15568
15656
|
}
|
|
15569
15657
|
},
|
|
15570
15658
|
StateMachineSnapshotMutation: {
|
|
15571
15659
|
snapshot: async (value) => await run(value.target.state_machine(value.name)),
|
|
15572
15660
|
activate_transition: async (value, { name }) => {
|
|
15573
|
-
await run(
|
|
15661
|
+
await run(
|
|
15662
|
+
value.target.activate_state_machine_transition(value.name, name)
|
|
15663
|
+
);
|
|
15574
15664
|
return value;
|
|
15575
15665
|
}
|
|
15576
15666
|
},
|
|
@@ -15601,7 +15691,11 @@ var stateMachineMetamodelPackage = defineMetamodelPackage({
|
|
|
15601
15691
|
reachable_states: (value) => value.reachable_states,
|
|
15602
15692
|
is_final: (value) => value.is_final,
|
|
15603
15693
|
history: (value) => value.history,
|
|
15604
|
-
paths_to: async (value, { state }) => await stateMachines.pathsToState(
|
|
15694
|
+
paths_to: async (value, { state }) => await stateMachines.pathsToState(
|
|
15695
|
+
value.model.target || value.model,
|
|
15696
|
+
value.name,
|
|
15697
|
+
state
|
|
15698
|
+
)
|
|
15605
15699
|
},
|
|
15606
15700
|
StateMachine: {
|
|
15607
15701
|
name: (value) => value.name,
|
|
@@ -15614,8 +15708,16 @@ var stateMachineMetamodelPackage = defineMetamodelPackage({
|
|
|
15614
15708
|
reachable_states: (value) => value.reachable_states,
|
|
15615
15709
|
is_final: (value) => value.is_final,
|
|
15616
15710
|
history: (value) => value.history,
|
|
15617
|
-
paths_to: async (value, { state }) => await stateMachines.pathsToState(
|
|
15618
|
-
|
|
15711
|
+
paths_to: async (value, { state }) => await stateMachines.pathsToState(
|
|
15712
|
+
value.model.target || value.model,
|
|
15713
|
+
value.name,
|
|
15714
|
+
state
|
|
15715
|
+
),
|
|
15716
|
+
instances_in_state: async (value, { state }) => await stateMachines.instancesInState(
|
|
15717
|
+
value.model.target || value.model,
|
|
15718
|
+
value.name,
|
|
15719
|
+
state
|
|
15720
|
+
)
|
|
15619
15721
|
}
|
|
15620
15722
|
};
|
|
15621
15723
|
}
|
|
@@ -15659,9 +15761,12 @@ var stateMachineMetamodelPackage = defineMetamodelPackage({
|
|
|
15659
15761
|
// ../metamodel-validation-rule/src/index.ts
|
|
15660
15762
|
function describeRule(rule) {
|
|
15661
15763
|
if (rule.message) return rule.message;
|
|
15662
|
-
if (rule.stringValue !== void 0)
|
|
15663
|
-
|
|
15664
|
-
if (rule.
|
|
15764
|
+
if (rule.stringValue !== void 0)
|
|
15765
|
+
return `${rule.operator} ${JSON.stringify(rule.stringValue)}`;
|
|
15766
|
+
if (rule.numberValue !== void 0)
|
|
15767
|
+
return `${rule.operator} ${rule.numberValue}`;
|
|
15768
|
+
if (rule.booleanValue !== void 0)
|
|
15769
|
+
return `${rule.operator} ${String(rule.booleanValue)}`;
|
|
15665
15770
|
return rule.operator;
|
|
15666
15771
|
}
|
|
15667
15772
|
function normalizeRule(rule) {
|
|
@@ -15787,10 +15892,14 @@ var validationRuleMetamodelPackage = defineMetamodelPackage({
|
|
|
15787
15892
|
},
|
|
15788
15893
|
summary: {
|
|
15789
15894
|
selections: {
|
|
15790
|
-
propertyFields: [
|
|
15895
|
+
propertyFields: [
|
|
15896
|
+
`validation_rules { operator string_value number_value boolean_value message }`
|
|
15897
|
+
]
|
|
15791
15898
|
},
|
|
15792
15899
|
readPropertySummary(rawProperty) {
|
|
15793
|
-
const rules = Array.isArray(rawProperty.validation_rules) ? rawProperty.validation_rules.map(normalizeRule).filter(
|
|
15900
|
+
const rules = Array.isArray(rawProperty.validation_rules) ? rawProperty.validation_rules.map(normalizeRule).filter(
|
|
15901
|
+
(rule) => Boolean(rule)
|
|
15902
|
+
) : [];
|
|
15794
15903
|
return {
|
|
15795
15904
|
validationRules: rules
|
|
15796
15905
|
};
|
|
@@ -16323,7 +16432,7 @@ ${effectMetamodelTable}
|
|
|
16323
16432
|
|
|
16324
16433
|
| API | Purpose |
|
|
16325
16434
|
|-----|---------|
|
|
16326
|
-
| \`submitJob(code)\` | Run code in the sandbox; import from
|
|
16435
|
+
| \`submitJob(code)\` | Run code in the sandbox; import generated classes from \`@granular/domain/<Class>\` and actions from \`@granular/actions/backend\` or \`@granular/actions/frontend\`. |
|
|
16327
16436
|
| \`answerPrompt(...)\`, \`appendMessage(...)\` | Human-in-the-loop and conversation APIs. |
|
|
16328
16437
|
| \`getDomain()\`, \`getDomainTypes()\`, \`getDomainDocs()\`, \`getDomainDocumentation()\` | Domain summary and generated TypeScript / docs. |
|
|
16329
16438
|
| \`getEffects()\` / \`getTools()\`, \`session.on("effects:changed", ...)\` | Effect catalog and live updates. |
|
|
@@ -16656,7 +16765,7 @@ function generateSandboxAgentDoc(manifest, meta) {
|
|
|
16656
16765
|
"2. **Register** a handler whose `name` (and `className` / `static` when applicable) matches \u2014 use `granular.ontology('" + meta.sandboxId + "').effects.registerMany([...])` in your host process (see `granular-effects.ts` in starter projects)."
|
|
16657
16766
|
);
|
|
16658
16767
|
lines.push(
|
|
16659
|
-
"3. **Invoke** from sandbox code via
|
|
16768
|
+
"3. **Invoke** from sandbox code via Harness v3 runtime imports inside `submitJob`; the handler you registered runs in your process and the result returns to the job."
|
|
16660
16769
|
);
|
|
16661
16770
|
lines.push("");
|
|
16662
16771
|
lines.push(
|
|
@@ -16713,7 +16822,7 @@ function generateSandboxAgentDoc(manifest, meta) {
|
|
|
16713
16822
|
lines.push("## `submitJob` / sandbox code");
|
|
16714
16823
|
lines.push("");
|
|
16715
16824
|
lines.push(
|
|
16716
|
-
"Jobs run in the sandbox with generated classes from
|
|
16825
|
+
"Jobs run in the sandbox with generated classes from `@granular/domain/<Class>` and global backend actions from `@granular/actions/backend`. Class names are PascalCase."
|
|
16717
16826
|
);
|
|
16718
16827
|
lines.push("");
|
|
16719
16828
|
const pascalImports = classes.map((c) => toPascalCase(c.name));
|
|
@@ -16723,13 +16832,20 @@ function generateSandboxAgentDoc(manifest, meta) {
|
|
|
16723
16832
|
lines.push("```typescript");
|
|
16724
16833
|
lines.push(`const session = await env.sessions.create();
|
|
16725
16834
|
const job = await session.submitJob(\``);
|
|
16726
|
-
|
|
16727
|
-
(
|
|
16728
|
-
);
|
|
16729
|
-
|
|
16730
|
-
|
|
16835
|
+
if (pascalImports.length > 0) {
|
|
16836
|
+
for (const name of pascalImports) {
|
|
16837
|
+
lines.push(` import { ${name} } from "@granular/domain/${name}";`);
|
|
16838
|
+
}
|
|
16839
|
+
}
|
|
16840
|
+
if (effectNames.length > 0) {
|
|
16841
|
+
lines.push(
|
|
16842
|
+
` import { ${effectNames.join(", ")} } from "@granular/actions/backend";`
|
|
16843
|
+
);
|
|
16844
|
+
}
|
|
16845
|
+
if (pascalImports.length === 0 && effectNames.length === 0) {
|
|
16846
|
+
lines.push(` // import generated classes from @granular/domain/<Class>`);
|
|
16731
16847
|
} else {
|
|
16732
|
-
lines.push(` // import
|
|
16848
|
+
lines.push(` // import user-facing helpers from @granular/agent when needed`);
|
|
16733
16849
|
}
|
|
16734
16850
|
lines.push("");
|
|
16735
16851
|
if (classes.length > 0) {
|
|
@@ -16817,7 +16933,10 @@ const job = await session.submitJob(\``);
|
|
|
16817
16933
|
lines.push("");
|
|
16818
16934
|
return lines.join("\n");
|
|
16819
16935
|
}
|
|
16820
|
-
var MANIFEST_GUIDE_RELATIVE = path3__namespace.join(
|
|
16936
|
+
var MANIFEST_GUIDE_RELATIVE = path3__namespace.join(
|
|
16937
|
+
"docs",
|
|
16938
|
+
"granular-manifest.md"
|
|
16939
|
+
);
|
|
16821
16940
|
var SANDBOX_DOC_FILENAME = "GRANULAR_SANDBOX.md";
|
|
16822
16941
|
function getAgentsMdPath(projectRoot) {
|
|
16823
16942
|
return path3__namespace.join(projectRoot, "AGENTS.md");
|
|
@@ -16839,7 +16958,9 @@ function writeManifestGuideFile(projectRoot, projectName) {
|
|
|
16839
16958
|
}
|
|
16840
16959
|
function writeAgentsMdFile(projectRoot) {
|
|
16841
16960
|
const agentsPath = getAgentsMdPath(projectRoot);
|
|
16842
|
-
const block = generateGranularAgentsBlock({
|
|
16961
|
+
const block = generateGranularAgentsBlock({
|
|
16962
|
+
manifestGuidePath: MANIFEST_GUIDE_RELATIVE.replace(/\\/g, "/")
|
|
16963
|
+
});
|
|
16843
16964
|
const previous = fs__namespace.existsSync(agentsPath) ? fs__namespace.readFileSync(agentsPath, "utf-8") : "";
|
|
16844
16965
|
const merged = mergeGranularAgentsBlock(previous, block);
|
|
16845
16966
|
fs__namespace.writeFileSync(agentsPath, merged, "utf-8");
|
|
@@ -18077,7 +18198,10 @@ async function initCommand(projectName, options) {
|
|
|
18077
18198
|
}
|
|
18078
18199
|
var DEFAULT_LOCAL_API_KEY2 = "gn_sk_tenant_default_principal_local_e2e_00000000";
|
|
18079
18200
|
function promptSecret(question) {
|
|
18080
|
-
const rl = readline__namespace.createInterface({
|
|
18201
|
+
const rl = readline__namespace.createInterface({
|
|
18202
|
+
input: process.stdin,
|
|
18203
|
+
output: process.stdout
|
|
18204
|
+
});
|
|
18081
18205
|
return new Promise((resolve2) => {
|
|
18082
18206
|
rl.question(` ${question}: `, (answer) => {
|
|
18083
18207
|
rl.close();
|
|
@@ -18089,7 +18213,10 @@ function waitForEnter2(message) {
|
|
|
18089
18213
|
if (!process.stdin.isTTY || !process.stdout.isTTY) {
|
|
18090
18214
|
return Promise.resolve();
|
|
18091
18215
|
}
|
|
18092
|
-
const rl = readline__namespace.createInterface({
|
|
18216
|
+
const rl = readline__namespace.createInterface({
|
|
18217
|
+
input: process.stdin,
|
|
18218
|
+
output: process.stdout
|
|
18219
|
+
});
|
|
18093
18220
|
return new Promise((resolve2) => {
|
|
18094
18221
|
rl.question(` ${message}`, () => {
|
|
18095
18222
|
rl.close();
|
|
@@ -18119,14 +18246,20 @@ async function loginCommand(options = {}) {
|
|
|
18119
18246
|
apiKey = existing?.trim() || process.env.GRANULAR_LOCAL_API_KEY?.trim() || DEFAULT_LOCAL_API_KEY2;
|
|
18120
18247
|
info("Using local Granular API key for localhost development.");
|
|
18121
18248
|
} else if (options.manual) {
|
|
18122
|
-
dim(
|
|
18249
|
+
dim(
|
|
18250
|
+
"Get your API key at https://app.granular.software/w/default/api-keys"
|
|
18251
|
+
);
|
|
18123
18252
|
console.log();
|
|
18124
18253
|
apiKey = await promptSecret("Enter your API key");
|
|
18125
18254
|
} else {
|
|
18126
18255
|
const timeoutMs = options.timeout && options.timeout > 0 ? options.timeout * 1e3 : void 0;
|
|
18127
|
-
await waitForEnter2(
|
|
18256
|
+
await waitForEnter2(
|
|
18257
|
+
"Press Enter to open the login page in your browser..."
|
|
18258
|
+
);
|
|
18128
18259
|
console.log();
|
|
18129
|
-
const waiting = spinner(
|
|
18260
|
+
const waiting = spinner(
|
|
18261
|
+
"Opening browser and waiting for authentication..."
|
|
18262
|
+
);
|
|
18130
18263
|
try {
|
|
18131
18264
|
apiKey = await loginWithBrowser({
|
|
18132
18265
|
authBaseUrl: loadAuthUrl(),
|
|
@@ -18144,7 +18277,9 @@ async function loginCommand(options = {}) {
|
|
|
18144
18277
|
waiting.succeed(" Browser authentication completed.");
|
|
18145
18278
|
} catch (err) {
|
|
18146
18279
|
waiting.fail(` Browser authentication failed: ${err.message}`);
|
|
18147
|
-
dim(
|
|
18280
|
+
dim(
|
|
18281
|
+
"Retry with `granular login --manual` to paste an API key directly."
|
|
18282
|
+
);
|
|
18148
18283
|
process.exit(1);
|
|
18149
18284
|
}
|
|
18150
18285
|
}
|
|
@@ -18154,7 +18289,9 @@ async function loginCommand(options = {}) {
|
|
|
18154
18289
|
process.exit(1);
|
|
18155
18290
|
}
|
|
18156
18291
|
if (!apiKey.startsWith("sk_") && !apiKey.startsWith("gn_sk_")) {
|
|
18157
|
-
warn(
|
|
18292
|
+
warn(
|
|
18293
|
+
"API key does not look like a recognized Granular/WorkOS key. Continuing anyway..."
|
|
18294
|
+
);
|
|
18158
18295
|
}
|
|
18159
18296
|
const spinner2 = spinner("Validating...");
|
|
18160
18297
|
const api = new ApiClient(apiKey, apiUrl);
|
|
@@ -18171,12 +18308,7 @@ async function loginCommand(options = {}) {
|
|
|
18171
18308
|
console.log();
|
|
18172
18309
|
}
|
|
18173
18310
|
function cleanPermissionProfile(profile) {
|
|
18174
|
-
const {
|
|
18175
|
-
source,
|
|
18176
|
-
sourcePath,
|
|
18177
|
-
defaultsOwnerName,
|
|
18178
|
-
...rest
|
|
18179
|
-
} = profile;
|
|
18311
|
+
const { source, sourcePath, defaultsOwnerName, ...rest } = profile;
|
|
18180
18312
|
return rest;
|
|
18181
18313
|
}
|
|
18182
18314
|
function writePermissionProfileFiles(profiles) {
|
|
@@ -18221,14 +18353,16 @@ async function pullCommand(versionId) {
|
|
|
18221
18353
|
writeManifestFile(project);
|
|
18222
18354
|
spin.text = " Downloading permission profile files...";
|
|
18223
18355
|
const profileVersions = await api.getVersionPermissionProfiles(versionId);
|
|
18224
|
-
const pulledProfiles = (profileVersions.items || []).map((item) => item.profile).filter(
|
|
18356
|
+
const pulledProfiles = (profileVersions.items || []).map((item) => item.profile).filter(
|
|
18357
|
+
(profile) => Boolean(profile?.name)
|
|
18358
|
+
);
|
|
18225
18359
|
const writtenProfiles = writePermissionProfileFiles(pulledProfiles);
|
|
18226
18360
|
spin.succeed(" Manifest pulled successfully.");
|
|
18227
18361
|
console.log();
|
|
18228
18362
|
keyValue({
|
|
18229
18363
|
"Manifest ID": full.manifestId,
|
|
18230
18364
|
"Source Version": versionId,
|
|
18231
|
-
|
|
18365
|
+
Version: targetVersionLabel,
|
|
18232
18366
|
"Written to": "granular.json",
|
|
18233
18367
|
"Permission profiles": writtenProfiles.length > 0 ? writtenProfiles.join(", ") : "none"
|
|
18234
18368
|
});
|
|
@@ -18263,32 +18397,51 @@ async function buildCommand() {
|
|
|
18263
18397
|
}
|
|
18264
18398
|
process.exit(1);
|
|
18265
18399
|
}
|
|
18266
|
-
const syncingProfiles = spinner(
|
|
18400
|
+
const syncingProfiles = spinner(
|
|
18401
|
+
`Syncing ${permissionProfiles.length} permission profile file(s)...`
|
|
18402
|
+
);
|
|
18267
18403
|
try {
|
|
18268
|
-
const result = await api.syncPermissionProfileSources(
|
|
18269
|
-
|
|
18404
|
+
const result = await api.syncPermissionProfileSources(
|
|
18405
|
+
config.sandboxId,
|
|
18406
|
+
permissionProfiles
|
|
18407
|
+
);
|
|
18408
|
+
syncingProfiles.succeed(
|
|
18409
|
+
` Permission profiles synced: ${brand.secondary(result.digest)}`
|
|
18410
|
+
);
|
|
18270
18411
|
} catch (err) {
|
|
18271
|
-
syncingProfiles.fail(
|
|
18412
|
+
syncingProfiles.fail(
|
|
18413
|
+
` Failed to sync permission profiles: ${err.message}`
|
|
18414
|
+
);
|
|
18272
18415
|
process.exit(1);
|
|
18273
18416
|
}
|
|
18274
18417
|
const uploading = spinner(`Uploading manifest "${manifest.name}"...`);
|
|
18275
18418
|
let uploadedManifest;
|
|
18276
18419
|
try {
|
|
18277
18420
|
uploadedManifest = await api.uploadManifest(config.sandboxId, manifest);
|
|
18278
|
-
uploading.succeed(
|
|
18421
|
+
uploading.succeed(
|
|
18422
|
+
` Manifest uploaded: ${brand.secondary(uploadedManifest.manifestId)}`
|
|
18423
|
+
);
|
|
18279
18424
|
} catch (err) {
|
|
18280
18425
|
uploading.fail(` Failed to upload manifest: ${err.message}`);
|
|
18281
18426
|
process.exit(1);
|
|
18282
18427
|
}
|
|
18283
|
-
const building = spinner(
|
|
18428
|
+
const building = spinner(
|
|
18429
|
+
"Creating or reusing ontology version from manifest..."
|
|
18430
|
+
);
|
|
18284
18431
|
try {
|
|
18285
|
-
const version2 = await api.createVersion(
|
|
18432
|
+
const version2 = await api.createVersion(
|
|
18433
|
+
config.sandboxId,
|
|
18434
|
+
uploadedManifest.manifestId
|
|
18435
|
+
);
|
|
18286
18436
|
building.text = ` Running build for version... ${brand.muted(version2.buildId)}`;
|
|
18287
18437
|
const startTime = Date.now();
|
|
18288
|
-
const completed = await api.waitForVersionBuild(
|
|
18289
|
-
|
|
18290
|
-
|
|
18291
|
-
|
|
18438
|
+
const completed = await api.waitForVersionBuild(
|
|
18439
|
+
version2.buildId,
|
|
18440
|
+
(status) => {
|
|
18441
|
+
const elapsed = Math.round((Date.now() - startTime) / 1e3);
|
|
18442
|
+
building.text = ` Running build for version... ${brand.muted(`${status} (${elapsed}s)`)}`;
|
|
18443
|
+
}
|
|
18444
|
+
);
|
|
18292
18445
|
const totalTime = Math.round((Date.now() - startTime) / 1e3);
|
|
18293
18446
|
building.succeed(` Version ready in ${totalTime}s`);
|
|
18294
18447
|
const [tags, diff] = await Promise.all([
|
|
@@ -18308,11 +18461,11 @@ async function buildCommand() {
|
|
|
18308
18461
|
"Version ID": completed.buildId,
|
|
18309
18462
|
"Version Number": completed.versionNumber != null ? String(completed.versionNumber) : "N/A",
|
|
18310
18463
|
"Build Run": completed.buildRunId || completed.latestBuildRunId || "N/A",
|
|
18311
|
-
|
|
18312
|
-
|
|
18464
|
+
Manifest: uploadedManifest.manifestId,
|
|
18465
|
+
Result: completed.createdNewVersion ? "new version created" : "existing version reused, new build run recorded",
|
|
18313
18466
|
"Dev Tag": devTag?.targetVersionId === completed.buildId || devTag?.targetBuildId === completed.buildId ? "updated to this version" : devTag?.targetVersionId || devTag?.targetBuildId || "not set",
|
|
18314
18467
|
"Prod Tag": prodTag?.targetVersionId || prodTag?.targetBuildId || "not set",
|
|
18315
|
-
|
|
18468
|
+
Duration: `${totalTime}s`,
|
|
18316
18469
|
"Agent doc": "GRANULAR_SANDBOX.md"
|
|
18317
18470
|
});
|
|
18318
18471
|
if (diff?.diff?.summary) {
|
|
@@ -18325,7 +18478,9 @@ async function buildCommand() {
|
|
|
18325
18478
|
});
|
|
18326
18479
|
}
|
|
18327
18480
|
console.log();
|
|
18328
|
-
info(
|
|
18481
|
+
info(
|
|
18482
|
+
"Common path: keep working in your dev environment while dev follows the newest successful version. Prod only changes when you promote it."
|
|
18483
|
+
);
|
|
18329
18484
|
console.log();
|
|
18330
18485
|
} catch (err) {
|
|
18331
18486
|
building.fail(` Build failed: ${err.message}`);
|
|
@@ -18342,7 +18497,10 @@ async function deployCommand(options = {}) {
|
|
|
18342
18497
|
process.exit(1);
|
|
18343
18498
|
}
|
|
18344
18499
|
const api = new ApiClient(config.apiKey, config.apiUrl);
|
|
18345
|
-
step(
|
|
18500
|
+
step(
|
|
18501
|
+
options.prod ? "Deploy to dev and prod" : "Deploy to dev",
|
|
18502
|
+
`Sandbox ${config.sandboxId}`
|
|
18503
|
+
);
|
|
18346
18504
|
console.log();
|
|
18347
18505
|
const manifest = config.project.manifest;
|
|
18348
18506
|
const permissionProfiles = readPermissionProfileFiles();
|
|
@@ -18356,19 +18514,30 @@ async function deployCommand(options = {}) {
|
|
|
18356
18514
|
for (const error2 of localPolicyValidation.errors) error(` ${error2}`);
|
|
18357
18515
|
process.exit(1);
|
|
18358
18516
|
}
|
|
18359
|
-
const syncingProfiles = spinner(
|
|
18517
|
+
const syncingProfiles = spinner(
|
|
18518
|
+
`Syncing ${permissionProfiles.length} permission profile file(s)...`
|
|
18519
|
+
);
|
|
18360
18520
|
try {
|
|
18361
|
-
const result = await api.syncPermissionProfileSources(
|
|
18362
|
-
|
|
18521
|
+
const result = await api.syncPermissionProfileSources(
|
|
18522
|
+
config.sandboxId,
|
|
18523
|
+
permissionProfiles
|
|
18524
|
+
);
|
|
18525
|
+
syncingProfiles.succeed(
|
|
18526
|
+
` Permission profiles synced: ${brand.secondary(result.digest)}`
|
|
18527
|
+
);
|
|
18363
18528
|
} catch (err) {
|
|
18364
|
-
syncingProfiles.fail(
|
|
18529
|
+
syncingProfiles.fail(
|
|
18530
|
+
` Failed to sync permission profiles: ${err.message}`
|
|
18531
|
+
);
|
|
18365
18532
|
process.exit(1);
|
|
18366
18533
|
}
|
|
18367
18534
|
const uploading = spinner(`Uploading manifest "${manifest.name}"...`);
|
|
18368
18535
|
let uploadedManifest;
|
|
18369
18536
|
try {
|
|
18370
18537
|
uploadedManifest = await api.uploadManifest(config.sandboxId, manifest);
|
|
18371
|
-
uploading.succeed(
|
|
18538
|
+
uploading.succeed(
|
|
18539
|
+
` Manifest uploaded: ${brand.secondary(uploadedManifest.manifestId)}`
|
|
18540
|
+
);
|
|
18372
18541
|
} catch (err) {
|
|
18373
18542
|
uploading.fail(` Failed to upload manifest: ${err.message}`);
|
|
18374
18543
|
process.exit(1);
|
|
@@ -18381,7 +18550,9 @@ async function deployCommand(options = {}) {
|
|
|
18381
18550
|
]);
|
|
18382
18551
|
const devTag = tags.find((tag2) => tag2.name === "dev");
|
|
18383
18552
|
const prodTag = tags.find((tag2) => tag2.name === "prod");
|
|
18384
|
-
const existingVersion = versions.find(
|
|
18553
|
+
const existingVersion = versions.find(
|
|
18554
|
+
(version2) => version2.manifestDigest === uploadedManifest.digest
|
|
18555
|
+
) || null;
|
|
18385
18556
|
const existingVersionId = existingVersion?.buildId || null;
|
|
18386
18557
|
const devVersionId = devTag?.targetVersionId || devTag?.targetBuildId || null;
|
|
18387
18558
|
const reuseDevPromotion = Boolean(
|
|
@@ -18391,9 +18562,14 @@ async function deployCommand(options = {}) {
|
|
|
18391
18562
|
let totalTime = 0;
|
|
18392
18563
|
let deployedToDev = false;
|
|
18393
18564
|
if (reuseDevPromotion) {
|
|
18394
|
-
building.succeed(
|
|
18565
|
+
building.succeed(
|
|
18566
|
+
` Current manifest already matches ${brand.secondary(existingVersionId)} on dev.`
|
|
18567
|
+
);
|
|
18395
18568
|
} else {
|
|
18396
|
-
const version2 = await api.createVersion(
|
|
18569
|
+
const version2 = await api.createVersion(
|
|
18570
|
+
config.sandboxId,
|
|
18571
|
+
uploadedManifest.manifestId
|
|
18572
|
+
);
|
|
18397
18573
|
const startTime = Date.now();
|
|
18398
18574
|
building.text = ` Running build for version... ${brand.muted(version2.buildId)}`;
|
|
18399
18575
|
completed = await api.waitForVersionBuild(version2.buildId, (status) => {
|
|
@@ -18402,7 +18578,9 @@ async function deployCommand(options = {}) {
|
|
|
18402
18578
|
});
|
|
18403
18579
|
totalTime = Math.round((Date.now() - startTime) / 1e3);
|
|
18404
18580
|
deployedToDev = true;
|
|
18405
|
-
building.succeed(
|
|
18581
|
+
building.succeed(
|
|
18582
|
+
` Dev now points to ${brand.secondary(completed.buildId)} (${totalTime}s)`
|
|
18583
|
+
);
|
|
18406
18584
|
}
|
|
18407
18585
|
if (!completed) {
|
|
18408
18586
|
throw new Error("Could not resolve the current ontology version.");
|
|
@@ -18410,7 +18588,9 @@ async function deployCommand(options = {}) {
|
|
|
18410
18588
|
if (options.prod) {
|
|
18411
18589
|
const promoting = spinner("Promoting prod...");
|
|
18412
18590
|
await api.promoteVersionToProd(completed.buildId);
|
|
18413
|
-
promoting.succeed(
|
|
18591
|
+
promoting.succeed(
|
|
18592
|
+
` Prod now points to ${brand.secondary(completed.buildId)}`
|
|
18593
|
+
);
|
|
18414
18594
|
}
|
|
18415
18595
|
writeSandboxAgentDocFile(getProjectRoot(), manifest, {
|
|
18416
18596
|
sandboxId: config.sandboxId,
|
|
@@ -18422,18 +18602,20 @@ async function deployCommand(options = {}) {
|
|
|
18422
18602
|
success(`Deployed ${brand.bold(manifest.name)} successfully!`);
|
|
18423
18603
|
console.log();
|
|
18424
18604
|
keyValue({
|
|
18425
|
-
|
|
18605
|
+
Version: completed.buildId,
|
|
18426
18606
|
"Version Number": completed.versionNumber != null ? String(completed.versionNumber) : "N/A",
|
|
18427
18607
|
"Build Run": completed.buildRunId || completed.latestBuildRunId || "N/A",
|
|
18428
|
-
|
|
18608
|
+
Manifest: uploadedManifest.manifestId,
|
|
18429
18609
|
"Dev Tag": completed.buildId,
|
|
18430
18610
|
"Prod Tag": options.prod ? completed.buildId : prodTag?.targetVersionId || prodTag?.targetBuildId || "unchanged",
|
|
18431
|
-
|
|
18611
|
+
Release: options.prod ? deployedToDev ? "dev and prod now point to this version" : "prod promoted to the version already on dev" : "dev updated to this version",
|
|
18432
18612
|
"Agent doc": "GRANULAR_SANDBOX.md"
|
|
18433
18613
|
});
|
|
18434
18614
|
console.log();
|
|
18435
18615
|
if (options.prod) {
|
|
18436
|
-
info(
|
|
18616
|
+
info(
|
|
18617
|
+
"Existing prod environments keep their current version until they are transitioned."
|
|
18618
|
+
);
|
|
18437
18619
|
console.log();
|
|
18438
18620
|
}
|
|
18439
18621
|
} catch (err) {
|
|
@@ -18445,7 +18627,9 @@ async function deployCommand(options = {}) {
|
|
|
18445
18627
|
// src/cli/commands/status.ts
|
|
18446
18628
|
function summarizeLocalManifest(project) {
|
|
18447
18629
|
const manifest = project.manifest;
|
|
18448
|
-
const classCount = manifest.volumes.flatMap((volume) => volume.operations).filter(
|
|
18630
|
+
const classCount = manifest.volumes.flatMap((volume) => volume.operations).filter(
|
|
18631
|
+
(operation) => operation.create && !operation.defineRelationship
|
|
18632
|
+
).length;
|
|
18449
18633
|
const relationshipCount = manifest.volumes.flatMap((volume) => volume.operations).filter((operation) => operation.defineRelationship).length;
|
|
18450
18634
|
return {
|
|
18451
18635
|
name: manifest.name,
|
|
@@ -18515,8 +18699,8 @@ async function statusCommand(options = {}) {
|
|
|
18515
18699
|
console.log();
|
|
18516
18700
|
keyValue({
|
|
18517
18701
|
"Sandbox ID": sandbox.sandboxId,
|
|
18518
|
-
|
|
18519
|
-
|
|
18702
|
+
Name: sandbox.name,
|
|
18703
|
+
Created: sandbox.createdAt
|
|
18520
18704
|
});
|
|
18521
18705
|
console.log();
|
|
18522
18706
|
keyValue({
|
|
@@ -18557,9 +18741,9 @@ async function statusCommand(options = {}) {
|
|
|
18557
18741
|
step("Local Manifest", `granular.json`);
|
|
18558
18742
|
console.log();
|
|
18559
18743
|
keyValue({
|
|
18560
|
-
|
|
18561
|
-
|
|
18562
|
-
|
|
18744
|
+
Name: payload.localManifest.name,
|
|
18745
|
+
Classes: payload.localManifest.classCount,
|
|
18746
|
+
Relationships: payload.localManifest.relationshipCount
|
|
18563
18747
|
});
|
|
18564
18748
|
}
|
|
18565
18749
|
if (options.verbose) {
|
|
@@ -18580,7 +18764,10 @@ async function statusCommand(options = {}) {
|
|
|
18580
18764
|
}
|
|
18581
18765
|
}
|
|
18582
18766
|
function prompt2(question, defaultValue) {
|
|
18583
|
-
const rl = readline__namespace.createInterface({
|
|
18767
|
+
const rl = readline__namespace.createInterface({
|
|
18768
|
+
input: process.stdin,
|
|
18769
|
+
output: process.stdout
|
|
18770
|
+
});
|
|
18584
18771
|
const suffix = defaultValue ? ` ${brand.muted(`(${defaultValue})`)}` : "";
|
|
18585
18772
|
return new Promise((resolve2) => {
|
|
18586
18773
|
rl.question(` ${question}${suffix}: `, (answer) => {
|
|
@@ -18616,7 +18803,10 @@ async function addClassCommand(name) {
|
|
|
18616
18803
|
const fieldName = await prompt2("Field name (leave empty to finish)");
|
|
18617
18804
|
if (!fieldName) break;
|
|
18618
18805
|
const fieldType = await prompt2("Type", "string");
|
|
18619
|
-
const description = await prompt2(
|
|
18806
|
+
const description = await prompt2(
|
|
18807
|
+
"Description",
|
|
18808
|
+
`${fieldName} of the ${name}`
|
|
18809
|
+
);
|
|
18620
18810
|
fields[fieldName] = {
|
|
18621
18811
|
type: fieldType,
|
|
18622
18812
|
description
|
|
@@ -18635,9 +18825,7 @@ async function addClassCommand(name) {
|
|
|
18635
18825
|
manifest.volumes.push({
|
|
18636
18826
|
name: "schema",
|
|
18637
18827
|
scope: "sandbox",
|
|
18638
|
-
imports: [
|
|
18639
|
-
{ alias: "@std", name: "standard_modules", label: "prod" }
|
|
18640
|
-
],
|
|
18828
|
+
imports: [{ alias: "@std", name: "standard_modules", label: "prod" }],
|
|
18641
18829
|
operations: []
|
|
18642
18830
|
});
|
|
18643
18831
|
}
|
|
@@ -18647,7 +18835,10 @@ async function addClassCommand(name) {
|
|
|
18647
18835
|
success(`Class ${brand.bold(name)} added to granular.json`);
|
|
18648
18836
|
dim(`${Object.keys(fields).length} field(s) defined`);
|
|
18649
18837
|
nextSteps([
|
|
18650
|
-
{
|
|
18838
|
+
{
|
|
18839
|
+
command: `granular add field ${name} <field>`,
|
|
18840
|
+
description: "Add more fields"
|
|
18841
|
+
},
|
|
18651
18842
|
{ command: "granular add relation", description: "Define a relationship" },
|
|
18652
18843
|
{ command: "granular build", description: "Build your changes" }
|
|
18653
18844
|
]);
|
|
@@ -18672,14 +18863,19 @@ async function addFieldCommand(className, fieldName) {
|
|
|
18672
18863
|
process.exit(1);
|
|
18673
18864
|
}
|
|
18674
18865
|
const fieldType = await prompt2("Type", "string");
|
|
18675
|
-
const description = await prompt2(
|
|
18866
|
+
const description = await prompt2(
|
|
18867
|
+
"Description",
|
|
18868
|
+
`${fieldName} of the ${className}`
|
|
18869
|
+
);
|
|
18676
18870
|
if (!classOp.has) classOp.has = {};
|
|
18677
18871
|
classOp.has[fieldName] = {
|
|
18678
18872
|
type: fieldType,
|
|
18679
18873
|
description
|
|
18680
18874
|
};
|
|
18681
18875
|
writeManifestFile(project);
|
|
18682
|
-
success(
|
|
18876
|
+
success(
|
|
18877
|
+
`Field ${brand.bold(fieldName)} (${fieldType}) added to ${brand.bold(className)}`
|
|
18878
|
+
);
|
|
18683
18879
|
nextSteps([
|
|
18684
18880
|
{ command: "granular build", description: "Build your changes" }
|
|
18685
18881
|
]);
|
|
@@ -18708,10 +18904,19 @@ async function addRelationCommand() {
|
|
|
18708
18904
|
error(`Class "${right}" not found.`);
|
|
18709
18905
|
process.exit(1);
|
|
18710
18906
|
}
|
|
18711
|
-
const leftSubmodel = await prompt2(
|
|
18907
|
+
const leftSubmodel = await prompt2(
|
|
18908
|
+
`${left}'s reference to ${right} (plural)`,
|
|
18909
|
+
`${right}s`
|
|
18910
|
+
);
|
|
18712
18911
|
const rightSubmodel = await prompt2(`${right}'s reference to ${left}`, left);
|
|
18713
|
-
const leftIsMany = await confirm2(
|
|
18714
|
-
|
|
18912
|
+
const leftIsMany = await confirm2(
|
|
18913
|
+
`Can a ${left} have multiple ${leftSubmodel}?`,
|
|
18914
|
+
true
|
|
18915
|
+
);
|
|
18916
|
+
const rightIsMany = await confirm2(
|
|
18917
|
+
`Can a ${right} have multiple ${rightSubmodel}?`,
|
|
18918
|
+
false
|
|
18919
|
+
);
|
|
18715
18920
|
const operation = {
|
|
18716
18921
|
defineRelationship: {
|
|
18717
18922
|
left,
|
|
@@ -18725,8 +18930,12 @@ async function addRelationCommand() {
|
|
|
18725
18930
|
manifest.volumes[0].operations.push(operation);
|
|
18726
18931
|
writeManifestFile(project);
|
|
18727
18932
|
console.log();
|
|
18728
|
-
success(
|
|
18729
|
-
|
|
18933
|
+
success(
|
|
18934
|
+
`Relationship defined: ${brand.bold(left)} \u2194 ${brand.bold(right)}`
|
|
18935
|
+
);
|
|
18936
|
+
dim(
|
|
18937
|
+
`${left}.${leftSubmodel} (${leftIsMany ? "many" : "one"}) \u2194 ${right}.${rightSubmodel} (${rightIsMany ? "many" : "one"})`
|
|
18938
|
+
);
|
|
18730
18939
|
nextSteps([
|
|
18731
18940
|
{ command: "granular build", description: "Build your changes" }
|
|
18732
18941
|
]);
|
|
@@ -18760,7 +18969,7 @@ async function whoamiCommand() {
|
|
|
18760
18969
|
try {
|
|
18761
18970
|
const sandbox = await api.getSandbox(rc.sandboxId);
|
|
18762
18971
|
keyValue({
|
|
18763
|
-
|
|
18972
|
+
Sandbox: sandbox.name || sandbox.sandboxId,
|
|
18764
18973
|
"Sandbox ID": sandbox.sandboxId,
|
|
18765
18974
|
"API URL": config.apiUrl
|
|
18766
18975
|
});
|
|
@@ -18802,16 +19011,24 @@ async function devCommand() {
|
|
|
18802
19011
|
return;
|
|
18803
19012
|
}
|
|
18804
19013
|
const spin = spinner("Uploading manifest...");
|
|
18805
|
-
const uploaded = await api.uploadManifest(
|
|
19014
|
+
const uploaded = await api.uploadManifest(
|
|
19015
|
+
config.sandboxId,
|
|
19016
|
+
project.manifest
|
|
19017
|
+
);
|
|
18806
19018
|
spin.text = " Building...";
|
|
18807
|
-
const build = await api.triggerBuild(
|
|
19019
|
+
const build = await api.triggerBuild(
|
|
19020
|
+
config.sandboxId,
|
|
19021
|
+
uploaded.manifestId
|
|
19022
|
+
);
|
|
18808
19023
|
const startTime = Date.now();
|
|
18809
19024
|
const completed = await api.waitForBuild(build.buildId, (status) => {
|
|
18810
19025
|
const elapsed = Math.round((Date.now() - startTime) / 1e3);
|
|
18811
19026
|
spin.text = ` Building... ${brand.muted(`${status} (${elapsed}s)`)}`;
|
|
18812
19027
|
});
|
|
18813
19028
|
const totalTime = Math.round((Date.now() - startTime) / 1e3);
|
|
18814
|
-
spin.succeed(
|
|
19029
|
+
spin.succeed(
|
|
19030
|
+
` Build completed in ${totalTime}s \u2014 ${brand.secondary(completed.buildId)}`
|
|
19031
|
+
);
|
|
18815
19032
|
writeSandboxAgentDocFile(getProjectRoot(), project.manifest, {
|
|
18816
19033
|
sandboxId: config.sandboxId,
|
|
18817
19034
|
apiUrl: config.apiUrl,
|
|
@@ -18860,7 +19077,9 @@ async function documentCommand() {
|
|
|
18860
19077
|
buildPending: true
|
|
18861
19078
|
});
|
|
18862
19079
|
spinner2.succeed(`Generated ${brand.bold("GRANULAR_SANDBOX.md")}`);
|
|
18863
|
-
info(
|
|
19080
|
+
info(
|
|
19081
|
+
"This file lists classes, relationships, and effects from granular.json. After a successful build, run again or use `granular build` to fill in manifest and build ids."
|
|
19082
|
+
);
|
|
18864
19083
|
}
|
|
18865
19084
|
var SIMULATOR_BASE = "https://app.granular.software/simulator";
|
|
18866
19085
|
function openUrl(url) {
|
|
@@ -18871,7 +19090,9 @@ function openUrl(url) {
|
|
|
18871
19090
|
async function simulateCommand(sandboxIdArg, options) {
|
|
18872
19091
|
const sandboxId = sandboxIdArg ?? resolveConfig().sandboxId;
|
|
18873
19092
|
if (!sandboxId) {
|
|
18874
|
-
error(
|
|
19093
|
+
error(
|
|
19094
|
+
"No ontology ID. Run from a project with `granular init` or pass one explicitly: granular simulate <ontology-id>"
|
|
19095
|
+
);
|
|
18875
19096
|
process.exit(1);
|
|
18876
19097
|
}
|
|
18877
19098
|
const params = new URLSearchParams({
|
|
@@ -18909,6 +19130,7 @@ var DEBUG_WS = process.env.GRANULAR_DEBUG_WS === "1";
|
|
|
18909
19130
|
var DEFAULT_RPC_TIMEOUT_MS = 3e4;
|
|
18910
19131
|
var DOMAIN_PACKAGE_RPC_TIMEOUT_MS = 12e4;
|
|
18911
19132
|
var EFFECT_CONTROL_RPC_TIMEOUT_MS = 12e4;
|
|
19133
|
+
var HARNESS_RUN_RPC_TIMEOUT_MS = 6e5;
|
|
18912
19134
|
var DEFAULT_RECONNECT_DELAY_MS = 3e3;
|
|
18913
19135
|
var DEFAULT_MAX_RECONNECT_ATTEMPTS = 5;
|
|
18914
19136
|
function debugWs(...args) {
|
|
@@ -18925,6 +19147,8 @@ function rpcTimeoutMsForMethod(method) {
|
|
|
18925
19147
|
case "effects.publishCatalog":
|
|
18926
19148
|
case "effects.refresh":
|
|
18927
19149
|
return EFFECT_CONTROL_RPC_TIMEOUT_MS;
|
|
19150
|
+
case "harness.run":
|
|
19151
|
+
return HARNESS_RUN_RPC_TIMEOUT_MS;
|
|
18928
19152
|
default:
|
|
18929
19153
|
return DEFAULT_RPC_TIMEOUT_MS;
|
|
18930
19154
|
}
|
|
@@ -19590,7 +19814,9 @@ function scorePromptChoiceMatch(answer, answerTokens, option) {
|
|
|
19590
19814
|
const choice = normalizePromptChoiceOption(option);
|
|
19591
19815
|
const { value, label } = choice;
|
|
19592
19816
|
const description = choice.description || "";
|
|
19593
|
-
const haystack = normalizePromptText(
|
|
19817
|
+
const haystack = normalizePromptText(
|
|
19818
|
+
[value, label, description].filter(Boolean).join(" ")
|
|
19819
|
+
);
|
|
19594
19820
|
if (!haystack) return { score: 0, resolvedValue: value || label || null };
|
|
19595
19821
|
let score = 0;
|
|
19596
19822
|
if (value && normalizePromptText(value) === answer) score += 12;
|
|
@@ -19600,7 +19826,8 @@ function scorePromptChoiceMatch(answer, answerTokens, option) {
|
|
|
19600
19826
|
for (const token of answerTokens) {
|
|
19601
19827
|
if (value && normalizePromptText(value).includes(token)) score += 10;
|
|
19602
19828
|
if (label && normalizePromptText(label).includes(token)) score += 8;
|
|
19603
|
-
if (description && normalizePromptText(description).includes(token))
|
|
19829
|
+
if (description && normalizePromptText(description).includes(token))
|
|
19830
|
+
score += 5;
|
|
19604
19831
|
}
|
|
19605
19832
|
return { score, resolvedValue: value || label || null };
|
|
19606
19833
|
}
|
|
@@ -19610,7 +19837,8 @@ function normalizePromptType(raw) {
|
|
|
19610
19837
|
const promptType = typeof raw?.promptType === "string" ? raw.promptType : null;
|
|
19611
19838
|
if (type === "confirm" || type === "choice" || type === "input") return type;
|
|
19612
19839
|
if (kind === "confirm" || kind === "choice" || kind === "input") return kind;
|
|
19613
|
-
if (promptType === "confirm" || promptType === "choice" || promptType === "input")
|
|
19840
|
+
if (promptType === "confirm" || promptType === "choice" || promptType === "input")
|
|
19841
|
+
return promptType;
|
|
19614
19842
|
return "input";
|
|
19615
19843
|
}
|
|
19616
19844
|
function normalizePrompt(rawValue) {
|
|
@@ -19626,7 +19854,9 @@ function normalizePrompt(rawValue) {
|
|
|
19626
19854
|
title: typeof source.title === "string" ? source.title : "Input required",
|
|
19627
19855
|
message: typeof source.message === "string" ? source.message : "",
|
|
19628
19856
|
options: Array.isArray(source.options) ? source.options.map(
|
|
19629
|
-
(option) => typeof option === "string" || asRecord(option) ? normalizePromptChoiceOption(
|
|
19857
|
+
(option) => typeof option === "string" || asRecord(option) ? normalizePromptChoiceOption(
|
|
19858
|
+
option
|
|
19859
|
+
) : option
|
|
19630
19860
|
) : void 0,
|
|
19631
19861
|
defaultValue: source.defaultValue,
|
|
19632
19862
|
placeholder: typeof source.placeholder === "string" ? source.placeholder : void 0,
|
|
@@ -19638,13 +19868,17 @@ function resolvePromptAnswer(prompt3, answer) {
|
|
|
19638
19868
|
if (!prompt3) return answer;
|
|
19639
19869
|
if (prompt3.type === "confirm") {
|
|
19640
19870
|
if (typeof answer === "boolean") return answer;
|
|
19641
|
-
if (typeof answer === "string")
|
|
19871
|
+
if (typeof answer === "string")
|
|
19872
|
+
return /^(yes|y|true|confirm|ok)/i.test(answer.trim());
|
|
19642
19873
|
return Boolean(answer);
|
|
19643
19874
|
}
|
|
19644
19875
|
if (prompt3.type === "choice" && Array.isArray(prompt3.options) && typeof answer === "string") {
|
|
19645
19876
|
const normalized = normalizePromptText(answer);
|
|
19646
19877
|
const tokens = extractPromptTokens(answer);
|
|
19647
|
-
let best = {
|
|
19878
|
+
let best = {
|
|
19879
|
+
score: -1,
|
|
19880
|
+
resolvedValue: null
|
|
19881
|
+
};
|
|
19648
19882
|
for (const option of prompt3.options) {
|
|
19649
19883
|
const scored = scorePromptChoiceMatch(normalized, tokens, option);
|
|
19650
19884
|
if (scored.score > best.score) best = scored;
|
|
@@ -19820,9 +20054,11 @@ var Session = class {
|
|
|
19820
20054
|
/**
|
|
19821
20055
|
* Submit a job to execute code in the sandbox.
|
|
19822
20056
|
*
|
|
19823
|
-
* The code can import typed classes from
|
|
20057
|
+
* The code can import typed classes from Harness v3 runtime modules:
|
|
19824
20058
|
* ```typescript
|
|
19825
|
-
* import { Author
|
|
20059
|
+
* import { Author } from "@granular/domain/Author";
|
|
20060
|
+
* import { Book } from "@granular/domain/Book";
|
|
20061
|
+
* import { global_search } from "@granular/actions/backend";
|
|
19826
20062
|
*
|
|
19827
20063
|
* const totalAuthors = await Author.count();
|
|
19828
20064
|
* const firstAuthorsPage = await Author.page({ page: 1, perPage: 10, saveAs: 'recent_authors' });
|
|
@@ -19900,7 +20136,11 @@ var Session = class {
|
|
|
19900
20136
|
const resolvedAnswer = resolvePromptAnswer(prompt3, answer);
|
|
19901
20137
|
this.promptCache.delete(promptId);
|
|
19902
20138
|
this.hiddenPromptIds.add(promptId);
|
|
19903
|
-
this.emit("prompt", {
|
|
20139
|
+
this.emit("prompt:answered", {
|
|
20140
|
+
...prompt3 || { id: promptId },
|
|
20141
|
+
id: promptId,
|
|
20142
|
+
status: "answered"
|
|
20143
|
+
});
|
|
19904
20144
|
try {
|
|
19905
20145
|
const response = await this.client.call("prompt.answer", {
|
|
19906
20146
|
promptId,
|
|
@@ -20211,14 +20451,19 @@ var Session = class {
|
|
|
20211
20451
|
const tools = summary.tools || [];
|
|
20212
20452
|
if (classes && Object.keys(classes).length > 0) {
|
|
20213
20453
|
let docs2 = "# Domain Documentation\n\n";
|
|
20214
|
-
docs2 += "Import classes and
|
|
20454
|
+
docs2 += "Import concrete classes from `@granular/domain/<Class>` and global backend actions from `@granular/actions/backend`:\n\n";
|
|
20215
20455
|
const classNames = Object.keys(classes).map(
|
|
20216
20456
|
(c) => c.charAt(0).toUpperCase() + c.slice(1)
|
|
20217
20457
|
);
|
|
20218
20458
|
const globalNames = (globalTools || []).map((t) => t.name);
|
|
20219
|
-
const
|
|
20459
|
+
const importLines = [
|
|
20460
|
+
...classNames.map(
|
|
20461
|
+
(name) => `import { ${name} } from "@granular/domain/${name}";`
|
|
20462
|
+
),
|
|
20463
|
+
globalNames.length > 0 ? `import { ${globalNames.join(", ")} } from "@granular/actions/backend";` : null
|
|
20464
|
+
].filter(Boolean);
|
|
20220
20465
|
docs2 += `\`\`\`typescript
|
|
20221
|
-
|
|
20466
|
+
${importLines.join("\n") || "// No generated domain imports available."}
|
|
20222
20467
|
\`\`\`
|
|
20223
20468
|
|
|
20224
20469
|
`;
|
|
@@ -20282,10 +20527,13 @@ import { ${allImports} } from "./sandbox-tools";
|
|
|
20282
20527
|
return "No effects available in this domain.";
|
|
20283
20528
|
}
|
|
20284
20529
|
let docs = "# Available Effects\n\n";
|
|
20285
|
-
docs += "Import
|
|
20286
|
-
docs +=
|
|
20530
|
+
docs += "Import global backend actions from `@granular/actions/backend` and call them with await:\n\n";
|
|
20531
|
+
docs += `\`\`\`typescript
|
|
20532
|
+
import { ${tools[0]?.name || "example"} } from "@granular/actions/backend";
|
|
20533
|
+
|
|
20534
|
+
`;
|
|
20287
20535
|
docs += "// Example:\n";
|
|
20288
|
-
docs += `const result = await
|
|
20536
|
+
docs += `const result = await ${tools[0]?.name || "example"}(input);
|
|
20289
20537
|
`;
|
|
20290
20538
|
docs += "```\n\n";
|
|
20291
20539
|
for (const tool of tools) {
|
|
@@ -20416,7 +20664,7 @@ import { ${allImports} } from "./sandbox-tools";
|
|
|
20416
20664
|
const prompt3 = normalizePrompt(payload);
|
|
20417
20665
|
if (!prompt3) return;
|
|
20418
20666
|
if (this.hiddenPromptIds.has(prompt3.id)) {
|
|
20419
|
-
this.emit("prompt", { ...prompt3, status: "answered" });
|
|
20667
|
+
this.emit("prompt:answered", { ...prompt3, status: "answered" });
|
|
20420
20668
|
return;
|
|
20421
20669
|
}
|
|
20422
20670
|
this.promptCache.set(prompt3.id, prompt3);
|
|
@@ -20435,9 +20683,19 @@ import { ${allImports} } from "./sandbox-tools";
|
|
|
20435
20683
|
this.client.on("job.status", (data) => {
|
|
20436
20684
|
this.emit("job:status", data);
|
|
20437
20685
|
});
|
|
20686
|
+
this.client.on("harness.ui_status", (data) => {
|
|
20687
|
+
this.emit("harness:ui_status", data);
|
|
20688
|
+
});
|
|
20689
|
+
this.client.on("harness.model_stream", (data) => {
|
|
20690
|
+
this.emit("harness:model_stream", data);
|
|
20691
|
+
});
|
|
20692
|
+
this.client.on("harness.text_response.delta", (data) => {
|
|
20693
|
+
this.emit("harness:text_response_delta", data);
|
|
20694
|
+
});
|
|
20438
20695
|
this.client.on("job.agent_message", (data) => {
|
|
20439
20696
|
const normalized = normalizeJobAgentMessageEnvelope(data);
|
|
20440
20697
|
if (!normalized) return;
|
|
20698
|
+
this.emit("job:agent_message", normalized);
|
|
20441
20699
|
if (this.jobsMap.has(normalized.jobId)) return;
|
|
20442
20700
|
const pending = this.pendingAgentMessagesByJobId.get(normalized.jobId) || [];
|
|
20443
20701
|
if (normalized.message.messageId && pending.some(
|
|
@@ -20587,6 +20845,7 @@ function normalizeJobAgentMessageEnvelope(data) {
|
|
|
20587
20845
|
kind: d.kind === "artifacts" ? "artifacts" : "text",
|
|
20588
20846
|
reply: typeof d.reply === "string" ? d.reply : "",
|
|
20589
20847
|
show: d.show,
|
|
20848
|
+
actions: Array.isArray(d.actions) ? d.actions : void 0,
|
|
20590
20849
|
timestamp: d.timestamp || Date.now()
|
|
20591
20850
|
}
|
|
20592
20851
|
};
|
|
@@ -21598,7 +21857,9 @@ function resolveReverseEffect(effectMap, currentEffect, request, behaviors) {
|
|
|
21598
21857
|
return void 0;
|
|
21599
21858
|
}
|
|
21600
21859
|
function resolveHandlerForMode(effectMap, effect, request) {
|
|
21601
|
-
const behaviors = normalizeEffectBehaviors(
|
|
21860
|
+
const behaviors = normalizeEffectBehaviors(
|
|
21861
|
+
request.context?.behaviors || effect.metamodels || void 0
|
|
21862
|
+
);
|
|
21602
21863
|
const mode = resolveInvocationMode(request.context);
|
|
21603
21864
|
if (mode === "dryRun") {
|
|
21604
21865
|
if (effect.dryRunHandler) {
|
|
@@ -21613,7 +21874,12 @@ function resolveHandlerForMode(effectMap, effect, request) {
|
|
|
21613
21874
|
if (effect.reverseHandler) {
|
|
21614
21875
|
return { effect, mode, handler: effect.reverseHandler };
|
|
21615
21876
|
}
|
|
21616
|
-
const reverseEffect = resolveReverseEffect(
|
|
21877
|
+
const reverseEffect = resolveReverseEffect(
|
|
21878
|
+
effectMap,
|
|
21879
|
+
effect,
|
|
21880
|
+
request,
|
|
21881
|
+
behaviors
|
|
21882
|
+
);
|
|
21617
21883
|
if (reverseEffect) {
|
|
21618
21884
|
return {
|
|
21619
21885
|
effect: reverseEffect,
|
|
@@ -21621,7 +21887,9 @@ function resolveHandlerForMode(effectMap, effect, request) {
|
|
|
21621
21887
|
handler: reverseEffect.reverseHandler || reverseEffect.handler
|
|
21622
21888
|
};
|
|
21623
21889
|
}
|
|
21624
|
-
throw new Error(
|
|
21890
|
+
throw new Error(
|
|
21891
|
+
`Reverse execution is not supported for ${request.effectKey}`
|
|
21892
|
+
);
|
|
21625
21893
|
}
|
|
21626
21894
|
return { effect, mode, handler: effect.handler };
|
|
21627
21895
|
}
|
|
@@ -21637,7 +21905,9 @@ async function invokeRegisteredEffect(effectMap, request) {
|
|
|
21637
21905
|
const resolved = resolveHandlerForMode(effectMap, effect, request);
|
|
21638
21906
|
const context = {
|
|
21639
21907
|
...request.context || {},
|
|
21640
|
-
behaviors: normalizeEffectBehaviors(
|
|
21908
|
+
behaviors: normalizeEffectBehaviors(
|
|
21909
|
+
request.context?.behaviors || effect.metamodels || void 0
|
|
21910
|
+
),
|
|
21641
21911
|
invocation: {
|
|
21642
21912
|
mode: resolved.mode,
|
|
21643
21913
|
sourceEffectKey: request.effectKey,
|
|
@@ -21799,7 +22069,7 @@ function isRetryableRecordObjectsError(error2) {
|
|
|
21799
22069
|
}
|
|
21800
22070
|
function isRetryableEffectRegistrationError(error2) {
|
|
21801
22071
|
const message = error2 instanceof Error ? error2.message : String(error2);
|
|
21802
|
-
return /timed out|websocket disconnected|websocket not connected|rpc timeout|worker restarted mid-request|network connection lost|bad gateway|gateway timeout|too many requests|(?:control plane|granular|graphql) api error \((?:429|500|502|503|504)\)/i.test(
|
|
22072
|
+
return /timed out|websocket disconnected|websocket not connected|rpc timeout|rpc error: internal error; reference|worker restarted mid-request|network connection lost|bad gateway|gateway timeout|too many requests|(?:control plane|granular|graphql) api error \((?:429|500|502|503|504)\)/i.test(
|
|
21803
22073
|
message
|
|
21804
22074
|
);
|
|
21805
22075
|
}
|
|
@@ -22033,19 +22303,19 @@ function computeEffectRegistrationKey(effect) {
|
|
|
22033
22303
|
function buildEffectHostUrl(apiUrl, sandboxId, effectClientId, clientId, effectHostUrl) {
|
|
22034
22304
|
const overrideUrl = effectHostUrl || process.env.GRANULAR_EFFECT_HOST_URL || process.env.EFFECT_HOST_URL;
|
|
22035
22305
|
const api = new URL(apiUrl);
|
|
22036
|
-
const localRuntimeBase = process.env.RUNTIME_ORCHESTRATOR_URL ||
|
|
22306
|
+
const localRuntimeBase = process.env.RUNTIME_ORCHESTRATOR_URL || "";
|
|
22037
22307
|
const url = new URL(overrideUrl || localRuntimeBase || apiUrl);
|
|
22038
22308
|
if (url.protocol === "https:") {
|
|
22039
22309
|
url.protocol = "wss:";
|
|
22040
22310
|
} else if (url.protocol === "http:") {
|
|
22041
22311
|
url.protocol = "ws:";
|
|
22042
22312
|
}
|
|
22043
|
-
if (!overrideUrl && isLocalControlUrl(apiUrl) && api.pathname.endsWith("/granular")) {
|
|
22044
|
-
url.pathname = "/granular/
|
|
22313
|
+
if (!overrideUrl && isLocalControlUrl(apiUrl) && !localRuntimeBase && api.pathname.endsWith("/granular")) {
|
|
22314
|
+
url.pathname = "/granular/effects/connect";
|
|
22045
22315
|
} else if (url.pathname.endsWith("/granular/ws/connect")) {
|
|
22046
22316
|
url.pathname = url.pathname.replace(/\/ws\/connect$/, "/effects/connect");
|
|
22047
22317
|
} else if (url.pathname.endsWith("/granular")) {
|
|
22048
|
-
url.pathname = isLocalControlUrl(url.toString()) ? "/granular/orchestrator/effects/connect" : `${url.pathname.replace(/\/$/, "")}/effects/connect`;
|
|
22318
|
+
url.pathname = localRuntimeBase && isLocalControlUrl(url.toString()) ? "/granular/orchestrator/effects/connect" : `${url.pathname.replace(/\/$/, "")}/effects/connect`;
|
|
22049
22319
|
} else if (url.pathname.endsWith("/v2/ws/connect")) {
|
|
22050
22320
|
url.pathname = url.pathname.replace(/\/ws\/connect$/, "/effects/connect");
|
|
22051
22321
|
} else if (url.pathname.endsWith("/v2/ws")) {
|
|
@@ -22226,7 +22496,15 @@ var Environment = class _Environment {
|
|
|
22226
22496
|
create: async (options) => this.createSession(options),
|
|
22227
22497
|
connect: async (sessionId, options) => this.connectSession(sessionId, options),
|
|
22228
22498
|
reopen: async (sessionId, options) => this.reopenSession(sessionId, options),
|
|
22229
|
-
close: async (sessionId, session2) => this.closeSession(sessionId, session2)
|
|
22499
|
+
close: async (sessionId, session2) => this.closeSession(sessionId, session2),
|
|
22500
|
+
state: async (options) => this.getUserEnvironmentState(options),
|
|
22501
|
+
markRead: async (options) => this.markUserEnvironmentSessionsRead(options)
|
|
22502
|
+
};
|
|
22503
|
+
}
|
|
22504
|
+
get userEnvironmentState() {
|
|
22505
|
+
return {
|
|
22506
|
+
get: async (options) => this.getUserEnvironmentState(options),
|
|
22507
|
+
markRead: async (options) => this.markUserEnvironmentSessionsRead(options)
|
|
22230
22508
|
};
|
|
22231
22509
|
}
|
|
22232
22510
|
get data() {
|
|
@@ -22267,6 +22545,18 @@ var Environment = class _Environment {
|
|
|
22267
22545
|
}
|
|
22268
22546
|
return status === "closed" ? this.granular.listClosedSessions({ environmentId: this.environmentId }) : this.granular.listOpenSessions({ environmentId: this.environmentId });
|
|
22269
22547
|
}
|
|
22548
|
+
async getUserEnvironmentState(options = {}) {
|
|
22549
|
+
return this.granular.getUserEnvironmentState({
|
|
22550
|
+
...options,
|
|
22551
|
+
environmentId: this.environmentId
|
|
22552
|
+
});
|
|
22553
|
+
}
|
|
22554
|
+
async markUserEnvironmentSessionsRead(options) {
|
|
22555
|
+
return this.granular.markUserEnvironmentSessionsRead({
|
|
22556
|
+
...options,
|
|
22557
|
+
environmentId: this.environmentId
|
|
22558
|
+
});
|
|
22559
|
+
}
|
|
22270
22560
|
async createSession(options) {
|
|
22271
22561
|
return this.granular.createSession({
|
|
22272
22562
|
environmentId: this.environmentId,
|
|
@@ -22277,7 +22567,9 @@ var Environment = class _Environment {
|
|
|
22277
22567
|
async connectSession(sessionId, options) {
|
|
22278
22568
|
const session2 = await this.granular["connectSession"]({
|
|
22279
22569
|
sessionId,
|
|
22280
|
-
clientId: options?.clientId
|
|
22570
|
+
clientId: options?.clientId,
|
|
22571
|
+
maxReconnectAttempts: options?.maxReconnectAttempts,
|
|
22572
|
+
reconnectDelayMs: options?.reconnectDelayMs
|
|
22281
22573
|
});
|
|
22282
22574
|
if (session2.environmentId !== this.environmentId) {
|
|
22283
22575
|
await session2.disconnect().catch(() => {
|
|
@@ -24072,6 +24364,39 @@ var Granular = class _Granular {
|
|
|
24072
24364
|
async listClosedSessions(filters) {
|
|
24073
24365
|
return this.listSessionsForEnvironment(filters.environmentId, "closed");
|
|
24074
24366
|
}
|
|
24367
|
+
async getUserEnvironmentState(options) {
|
|
24368
|
+
const query = new URLSearchParams({
|
|
24369
|
+
environmentId: options.environmentId
|
|
24370
|
+
});
|
|
24371
|
+
if (options.sessionScope) {
|
|
24372
|
+
query.set("sessionScope", options.sessionScope);
|
|
24373
|
+
}
|
|
24374
|
+
if (options.status) {
|
|
24375
|
+
query.set("status", options.status);
|
|
24376
|
+
}
|
|
24377
|
+
if (typeof options.limit === "number") {
|
|
24378
|
+
query.set("limit", String(options.limit));
|
|
24379
|
+
}
|
|
24380
|
+
if (typeof options.offset === "number") {
|
|
24381
|
+
query.set("offset", String(options.offset));
|
|
24382
|
+
}
|
|
24383
|
+
const state = await this.request(
|
|
24384
|
+
`/sdk/user-environment-state?${query.toString()}`
|
|
24385
|
+
);
|
|
24386
|
+
return this.normalizeUserEnvironmentState(state);
|
|
24387
|
+
}
|
|
24388
|
+
async markUserEnvironmentSessionsRead(options) {
|
|
24389
|
+
const result = await this.request("/sdk/user-environment-state/read", {
|
|
24390
|
+
method: "POST",
|
|
24391
|
+
body: JSON.stringify({
|
|
24392
|
+
environmentId: options.environmentId,
|
|
24393
|
+
sessionId: options.sessionId,
|
|
24394
|
+
sessionIds: options.sessionIds,
|
|
24395
|
+
readAt: options.readAt
|
|
24396
|
+
})
|
|
24397
|
+
});
|
|
24398
|
+
return result.readAtBySessionId || {};
|
|
24399
|
+
}
|
|
24075
24400
|
async listSessionsForEnvironment(environmentId, status) {
|
|
24076
24401
|
const query = new URLSearchParams({ environmentId, status });
|
|
24077
24402
|
const res = await this.request(
|
|
@@ -24102,6 +24427,24 @@ var Granular = class _Granular {
|
|
|
24102
24427
|
toolCallCount: typeof row.toolCallCount === "number" ? row.toolCallCount : void 0
|
|
24103
24428
|
};
|
|
24104
24429
|
}
|
|
24430
|
+
normalizeUserEnvironmentState(state) {
|
|
24431
|
+
return {
|
|
24432
|
+
...state,
|
|
24433
|
+
sessions: Array.isArray(state.sessions) ? state.sessions.map((item) => ({
|
|
24434
|
+
...item,
|
|
24435
|
+
session: this.normalizeConversationSession(
|
|
24436
|
+
item.session
|
|
24437
|
+
)
|
|
24438
|
+
})) : [],
|
|
24439
|
+
attention: {
|
|
24440
|
+
prompts: Array.isArray(state.attention?.prompts) ? state.attention.prompts : [],
|
|
24441
|
+
count: typeof state.attention?.count === "number" ? state.attention.count : 0,
|
|
24442
|
+
activePrompt: state.attention?.activePrompt || null
|
|
24443
|
+
},
|
|
24444
|
+
unreadCount: typeof state.unreadCount === "number" ? state.unreadCount : 0,
|
|
24445
|
+
readAtBySessionId: state.readAtBySessionId || {}
|
|
24446
|
+
};
|
|
24447
|
+
}
|
|
24105
24448
|
static coerceIsoDate(value) {
|
|
24106
24449
|
if (value instanceof Date) {
|
|
24107
24450
|
return value.toISOString();
|
|
@@ -24144,7 +24487,10 @@ var Granular = class _Granular {
|
|
|
24144
24487
|
});
|
|
24145
24488
|
const envData = await this.environments.get(minted.environmentId);
|
|
24146
24489
|
const environment = this.bindEnvironmentHandle(envData);
|
|
24147
|
-
return this.bindWebSocketEnvironmentSession(environment, clientId, minted
|
|
24490
|
+
return this.bindWebSocketEnvironmentSession(environment, clientId, minted, {
|
|
24491
|
+
maxReconnectAttempts: options.maxReconnectAttempts,
|
|
24492
|
+
reconnectDelayMs: options.reconnectDelayMs
|
|
24493
|
+
});
|
|
24148
24494
|
}
|
|
24149
24495
|
async recordOpenAIUsageSpend(usage, context, options) {
|
|
24150
24496
|
return recordOpenAIUsageSpend({
|
|
@@ -24295,13 +24641,15 @@ var Granular = class _Granular {
|
|
|
24295
24641
|
const graphqlEndpoint = `${this.httpUrl}/orchestrator/graphql`;
|
|
24296
24642
|
return new Environment(this, envData, this.apiKey, graphqlEndpoint);
|
|
24297
24643
|
}
|
|
24298
|
-
async bindWebSocketEnvironmentSession(environment, clientId, session2) {
|
|
24644
|
+
async bindWebSocketEnvironmentSession(environment, clientId, session2, transportOptions = {}) {
|
|
24299
24645
|
const client = new WSClient({
|
|
24300
24646
|
url: session2.wsUrl,
|
|
24301
24647
|
sessionId: session2.sessionId,
|
|
24302
24648
|
token: session2.token,
|
|
24303
24649
|
tokenProvider: this.tokenProvider,
|
|
24304
24650
|
WebSocketCtor: this.WebSocketCtor,
|
|
24651
|
+
maxReconnectAttempts: transportOptions.maxReconnectAttempts,
|
|
24652
|
+
reconnectDelayMs: transportOptions.reconnectDelayMs,
|
|
24305
24653
|
onUnexpectedClose: this.onUnexpectedClose,
|
|
24306
24654
|
onReconnectError: this.onReconnectError
|
|
24307
24655
|
});
|
|
@@ -24668,7 +25016,10 @@ var Granular = class _Granular {
|
|
|
24668
25016
|
try {
|
|
24669
25017
|
const sandbox = await this.sandboxes.get(nameOrId);
|
|
24670
25018
|
return sandbox;
|
|
24671
|
-
} catch {
|
|
25019
|
+
} catch (error2) {
|
|
25020
|
+
if (nameOrId.startsWith("sbx_")) {
|
|
25021
|
+
throw error2;
|
|
25022
|
+
}
|
|
24672
25023
|
const sandboxes = await this.sandboxes.list();
|
|
24673
25024
|
const existing = sandboxes.items.find((s) => s.name === nameOrId);
|
|
24674
25025
|
if (existing) {
|
|
@@ -25774,7 +26125,7 @@ async function graphqlCommand(options) {
|
|
|
25774
26125
|
"Ontology ID": payload.ontologyId,
|
|
25775
26126
|
"Environment ID": payload.environmentId,
|
|
25776
26127
|
"Session ID": payload.sessionId,
|
|
25777
|
-
|
|
26128
|
+
OK: payload.ok ? "yes" : "no"
|
|
25778
26129
|
});
|
|
25779
26130
|
console.log();
|
|
25780
26131
|
console.log(JSON.stringify(result, null, 2));
|
|
@@ -25785,7 +26136,9 @@ async function graphqlCommand(options) {
|
|
|
25785
26136
|
|
|
25786
26137
|
// src/cli/commands/effects.ts
|
|
25787
26138
|
function sortEffects(effects2) {
|
|
25788
|
-
return [...effects2].sort(
|
|
26139
|
+
return [...effects2].sort(
|
|
26140
|
+
(left, right) => left.name.localeCompare(right.name)
|
|
26141
|
+
);
|
|
25789
26142
|
}
|
|
25790
26143
|
async function effectsListCommand(options) {
|
|
25791
26144
|
const emitJson = options.json === true;
|
|
@@ -25896,7 +26249,10 @@ async function effectsDiffCommand(options) {
|
|
|
25896
26249
|
// src/cli/commands/job.ts
|
|
25897
26250
|
async function jobRunCommand(options) {
|
|
25898
26251
|
const emitJson = options.json === true;
|
|
25899
|
-
const code = readJobCodeFromOptions({
|
|
26252
|
+
const code = readJobCodeFromOptions({
|
|
26253
|
+
file: options.file,
|
|
26254
|
+
inline: options.inline
|
|
26255
|
+
});
|
|
25900
26256
|
if (!emitJson) {
|
|
25901
26257
|
printHeader();
|
|
25902
26258
|
}
|
|
@@ -25951,7 +26307,7 @@ async function jobRunCommand(options) {
|
|
|
25951
26307
|
"Environment ID": payload.environmentId,
|
|
25952
26308
|
"Session ID": payload.sessionId,
|
|
25953
26309
|
"Job ID": payload.jobId,
|
|
25954
|
-
|
|
26310
|
+
Status: payload.status
|
|
25955
26311
|
});
|
|
25956
26312
|
console.log();
|
|
25957
26313
|
console.log(JSON.stringify(result, null, 2));
|
|
@@ -26100,7 +26456,9 @@ async function versionCurrentCommand(options) {
|
|
|
26100
26456
|
]);
|
|
26101
26457
|
const current = resolveCurrentVersion(builds, tags);
|
|
26102
26458
|
if (!current) {
|
|
26103
|
-
throw new Error(
|
|
26459
|
+
throw new Error(
|
|
26460
|
+
"No versions exist for this ontology yet. Run `granular build` first."
|
|
26461
|
+
);
|
|
26104
26462
|
}
|
|
26105
26463
|
const payload = {
|
|
26106
26464
|
ontologyId: config.sandboxId,
|
|
@@ -26120,9 +26478,12 @@ async function versionCurrentCommand(options) {
|
|
|
26120
26478
|
console.log();
|
|
26121
26479
|
keyValue({
|
|
26122
26480
|
"Ontology ID": payload.ontologyId,
|
|
26123
|
-
"Current version": formatVersionLabel(
|
|
26124
|
-
|
|
26125
|
-
|
|
26481
|
+
"Current version": formatVersionLabel(
|
|
26482
|
+
payload.versionId,
|
|
26483
|
+
payload.versionNumber
|
|
26484
|
+
),
|
|
26485
|
+
Status: payload.status,
|
|
26486
|
+
Created: payload.createdAt,
|
|
26126
26487
|
"Dev tag": payload.devVersionId || "not set",
|
|
26127
26488
|
"Prod tag": payload.prodVersionId || "not set"
|
|
26128
26489
|
});
|
|
@@ -26164,7 +26525,7 @@ async function versionDiffCommand(versionId, againstVersionId, options) {
|
|
|
26164
26525
|
spin?.succeed(" Version diff ready.");
|
|
26165
26526
|
console.log();
|
|
26166
26527
|
keyValue({
|
|
26167
|
-
|
|
26528
|
+
Version: formatVersionLabel(version2.buildId, version2.versionNumber),
|
|
26168
26529
|
"Compared against": baseVersion ? formatVersionLabel(baseVersion.buildId, baseVersion.versionNumber) : diffResult.againstVersionId || "previous version",
|
|
26169
26530
|
"Added operations": summary.added,
|
|
26170
26531
|
"Changed operations": summary.changed,
|
|
@@ -26255,17 +26616,21 @@ async function tagMoveCommand(tagName, versionId) {
|
|
|
26255
26616
|
spin.succeed(` ${tagName} now points to ${versionId}.`);
|
|
26256
26617
|
console.log();
|
|
26257
26618
|
keyValue({
|
|
26258
|
-
|
|
26619
|
+
Tag: moved.name,
|
|
26259
26620
|
"Previous version": previousVersionId || "not set",
|
|
26260
26621
|
"Current version": moved.targetVersionId || moved.targetBuildId || "not set",
|
|
26261
|
-
|
|
26622
|
+
Protected: moved.protected ? "yes" : "no"
|
|
26262
26623
|
});
|
|
26263
26624
|
console.log();
|
|
26264
26625
|
if (moved.name === "prod") {
|
|
26265
|
-
info(
|
|
26626
|
+
info(
|
|
26627
|
+
"Existing prod environments keep their current version until they are transitioned."
|
|
26628
|
+
);
|
|
26266
26629
|
console.log();
|
|
26267
26630
|
} else if (moved.name === "dev") {
|
|
26268
|
-
info(
|
|
26631
|
+
info(
|
|
26632
|
+
"dev is usually automatic. You only need to move it manually for exceptional release workflows."
|
|
26633
|
+
);
|
|
26269
26634
|
console.log();
|
|
26270
26635
|
}
|
|
26271
26636
|
} catch (err) {
|