@elevasis/sdk 0.4.0 → 0.4.1
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/cli.cjs +157 -105
- package/dist/index.d.ts +30 -23
- package/dist/index.js +92 -33
- package/dist/worker/index.js +12 -1
- package/package.json +1 -1
package/dist/cli.cjs
CHANGED
|
@@ -39750,11 +39750,12 @@ var ResourceRegistry = class {
|
|
|
39750
39750
|
*/
|
|
39751
39751
|
serializedCache;
|
|
39752
39752
|
/**
|
|
39753
|
-
*
|
|
39754
|
-
*
|
|
39755
|
-
*
|
|
39753
|
+
* Per-resource remote configuration (external deployments)
|
|
39754
|
+
* Key: "orgName/resourceId", Value: RemoteOrgConfig for that resource.
|
|
39755
|
+
* Tracks which individual resources were added at runtime via deploy pipeline.
|
|
39756
|
+
* Static and remote resources coexist in the same org.
|
|
39756
39757
|
*/
|
|
39757
|
-
|
|
39758
|
+
remoteResources = /* @__PURE__ */ new Map();
|
|
39758
39759
|
/**
|
|
39759
39760
|
* Validates registry on construction
|
|
39760
39761
|
* - Checks for duplicate resourceIds within organizations
|
|
@@ -39857,67 +39858,125 @@ var ResourceRegistry = class {
|
|
|
39857
39858
|
// Runtime Organization Registration (External Deployments)
|
|
39858
39859
|
// ============================================================================
|
|
39859
39860
|
/**
|
|
39860
|
-
* Register
|
|
39861
|
+
* Register external resources at runtime
|
|
39861
39862
|
*
|
|
39862
39863
|
* Called during deploy pipeline when an external developer deploys their bundle.
|
|
39863
|
-
*
|
|
39864
|
-
* for worker thread execution branching.
|
|
39864
|
+
* Merges the incoming stub definitions into the org's registry and stores
|
|
39865
|
+
* per-resource remote config for worker thread execution branching.
|
|
39865
39866
|
*
|
|
39866
|
-
*
|
|
39867
|
-
*
|
|
39867
|
+
* Static and remote resources coexist in the same org. If the org already
|
|
39868
|
+
* has static resources, the incoming remote resources are merged alongside them.
|
|
39869
|
+
* If redeploying (some resources already registered as remote for this org),
|
|
39870
|
+
* the previous remote resources are unregistered first.
|
|
39868
39871
|
*
|
|
39869
39872
|
* @param orgName - Organization name (used as registry key)
|
|
39870
39873
|
* @param org - Stub resource definitions (workflows/agents with placeholder handlers)
|
|
39871
39874
|
* @param remote - Remote configuration (bundle path, deployment ID, env vars)
|
|
39872
|
-
* @throws Error if
|
|
39875
|
+
* @throws Error if incoming resourceId conflicts with a static resource
|
|
39876
|
+
* @throws Error if incoming deployment contains duplicate resourceIds
|
|
39873
39877
|
*/
|
|
39874
39878
|
registerOrganization(orgName, org, remote) {
|
|
39875
|
-
|
|
39876
|
-
|
|
39879
|
+
const incomingWorkflowIds = (org.workflows ?? []).map((w) => w.config.resourceId);
|
|
39880
|
+
const incomingAgentIds = (org.agents ?? []).map((a) => a.config.resourceId);
|
|
39881
|
+
const incomingIds = [...incomingWorkflowIds, ...incomingAgentIds];
|
|
39882
|
+
const seen = /* @__PURE__ */ new Set();
|
|
39883
|
+
for (const id of incomingIds) {
|
|
39884
|
+
if (seen.has(id)) {
|
|
39885
|
+
throw new Error(
|
|
39886
|
+
`Duplicate resource ID '${id}' in deployment. Each resource must have a unique ID.`
|
|
39887
|
+
);
|
|
39888
|
+
}
|
|
39889
|
+
seen.add(id);
|
|
39877
39890
|
}
|
|
39878
|
-
if (this.
|
|
39891
|
+
if (this.isRemote(orgName)) {
|
|
39879
39892
|
this.unregisterOrganization(orgName);
|
|
39880
39893
|
}
|
|
39881
|
-
this.registry[orgName]
|
|
39882
|
-
|
|
39883
|
-
|
|
39894
|
+
const existingOrg = this.registry[orgName];
|
|
39895
|
+
if (existingOrg) {
|
|
39896
|
+
const staticWorkflowIds = new Set((existingOrg.workflows ?? []).map((w) => w.config.resourceId));
|
|
39897
|
+
const staticAgentIds = new Set((existingOrg.agents ?? []).map((a) => a.config.resourceId));
|
|
39898
|
+
for (const id of incomingIds) {
|
|
39899
|
+
if (staticWorkflowIds.has(id) || staticAgentIds.has(id)) {
|
|
39900
|
+
throw new Error(
|
|
39901
|
+
`Resource '${id}' already exists in '${orgName}' as an internal resource. External deployments cannot override internal resources.`
|
|
39902
|
+
);
|
|
39903
|
+
}
|
|
39904
|
+
}
|
|
39905
|
+
}
|
|
39906
|
+
if (existingOrg) {
|
|
39907
|
+
existingOrg.workflows = [...existingOrg.workflows ?? [], ...org.workflows ?? []];
|
|
39908
|
+
existingOrg.agents = [...existingOrg.agents ?? [], ...org.agents ?? []];
|
|
39909
|
+
} else {
|
|
39910
|
+
this.registry[orgName] = org;
|
|
39911
|
+
}
|
|
39912
|
+
for (const id of incomingIds) {
|
|
39913
|
+
this.remoteResources.set(`${orgName}/${id}`, remote);
|
|
39914
|
+
}
|
|
39915
|
+
this.serializedCache.set(orgName, serializeOrganization(this.registry[orgName]));
|
|
39884
39916
|
}
|
|
39885
39917
|
/**
|
|
39886
|
-
* Unregister
|
|
39918
|
+
* Unregister runtime-registered resources for an organization
|
|
39887
39919
|
*
|
|
39888
|
-
*
|
|
39889
|
-
* Static
|
|
39890
|
-
*
|
|
39920
|
+
* Removes only resources that were registered at runtime (via registerOrganization).
|
|
39921
|
+
* Static resources loaded at startup are preserved. If the org still has static
|
|
39922
|
+
* resources after removal, the serialization cache is rebuilt. If no resources
|
|
39923
|
+
* remain, the org is fully removed from the registry.
|
|
39924
|
+
* No-op if the org has no remote resources.
|
|
39891
39925
|
*
|
|
39892
|
-
* @param orgName - Organization name to unregister
|
|
39926
|
+
* @param orgName - Organization name to unregister remote resources from
|
|
39893
39927
|
*/
|
|
39894
39928
|
unregisterOrganization(orgName) {
|
|
39895
|
-
|
|
39896
|
-
|
|
39897
|
-
this.
|
|
39898
|
-
|
|
39929
|
+
const prefix = `${orgName}/`;
|
|
39930
|
+
const remoteIds = /* @__PURE__ */ new Set();
|
|
39931
|
+
for (const key of this.remoteResources.keys()) {
|
|
39932
|
+
if (key.startsWith(prefix)) {
|
|
39933
|
+
remoteIds.add(key.slice(prefix.length));
|
|
39934
|
+
this.remoteResources.delete(key);
|
|
39935
|
+
}
|
|
39936
|
+
}
|
|
39937
|
+
if (remoteIds.size === 0) return;
|
|
39938
|
+
const orgResources = this.registry[orgName];
|
|
39939
|
+
if (!orgResources) return;
|
|
39940
|
+
orgResources.workflows = (orgResources.workflows ?? []).filter(
|
|
39941
|
+
(w) => !remoteIds.has(w.config.resourceId)
|
|
39942
|
+
);
|
|
39943
|
+
orgResources.agents = (orgResources.agents ?? []).filter(
|
|
39944
|
+
(a) => !remoteIds.has(a.config.resourceId)
|
|
39945
|
+
);
|
|
39946
|
+
const remaining = (orgResources.workflows?.length ?? 0) + (orgResources.agents?.length ?? 0) + (orgResources.triggers?.length ?? 0) + (orgResources.integrations?.length ?? 0) + (orgResources.externalResources?.length ?? 0) + (orgResources.humanCheckpoints?.length ?? 0);
|
|
39947
|
+
if (remaining > 0) {
|
|
39948
|
+
this.serializedCache.set(orgName, serializeOrganization(orgResources));
|
|
39949
|
+
} else {
|
|
39950
|
+
delete this.registry[orgName];
|
|
39951
|
+
this.serializedCache.delete(orgName);
|
|
39952
|
+
}
|
|
39899
39953
|
}
|
|
39900
39954
|
/**
|
|
39901
|
-
* Get remote configuration for
|
|
39955
|
+
* Get remote configuration for a specific resource
|
|
39902
39956
|
*
|
|
39903
|
-
* Returns the RemoteOrgConfig if the
|
|
39904
|
-
* or null if it's a static
|
|
39957
|
+
* Returns the RemoteOrgConfig if the resource was registered at runtime,
|
|
39958
|
+
* or null if it's a static resource or doesn't exist.
|
|
39905
39959
|
* Used by the execution coordinator to branch between local and worker execution.
|
|
39906
39960
|
*
|
|
39907
39961
|
* @param orgName - Organization name
|
|
39962
|
+
* @param resourceId - Resource ID
|
|
39908
39963
|
* @returns Remote config or null
|
|
39909
39964
|
*/
|
|
39910
|
-
getRemoteConfig(orgName) {
|
|
39911
|
-
return this.
|
|
39965
|
+
getRemoteConfig(orgName, resourceId) {
|
|
39966
|
+
return this.remoteResources.get(`${orgName}/${resourceId}`) ?? null;
|
|
39912
39967
|
}
|
|
39913
39968
|
/**
|
|
39914
|
-
* Check if an organization
|
|
39969
|
+
* Check if an organization has any remote (externally deployed) resources
|
|
39915
39970
|
*
|
|
39916
39971
|
* @param orgName - Organization name
|
|
39917
|
-
* @returns true if the org
|
|
39972
|
+
* @returns true if the org has at least one runtime-registered resource
|
|
39918
39973
|
*/
|
|
39919
39974
|
isRemote(orgName) {
|
|
39920
|
-
|
|
39975
|
+
const prefix = `${orgName}/`;
|
|
39976
|
+
for (const key of this.remoteResources.keys()) {
|
|
39977
|
+
if (key.startsWith(prefix)) return true;
|
|
39978
|
+
}
|
|
39979
|
+
return false;
|
|
39921
39980
|
}
|
|
39922
39981
|
// ============================================================================
|
|
39923
39982
|
// Resource Manifest Accessors
|
|
@@ -40116,20 +40175,11 @@ function wrapAction(commandName, fn) {
|
|
|
40116
40175
|
// src/cli/commands/check.ts
|
|
40117
40176
|
var import_meta = {};
|
|
40118
40177
|
function registerCheckCommand(program3) {
|
|
40119
|
-
program3.command("check").description("Validate project resources against the ResourceRegistry\n Example: elevasis check --
|
|
40120
|
-
const configPath = options.config ?? "./elevasis.config.ts";
|
|
40178
|
+
program3.command("check").description("Validate project resources against the ResourceRegistry\n Example: elevasis check --entry ./src/index.ts").option("--entry <path>", "Path to entry file (default: ./src/index.ts)").action(wrapAction("check", async (options) => {
|
|
40121
40179
|
const entryPath = options.entry ?? "./src/index.ts";
|
|
40122
40180
|
const spinner = ora("Validating resources...").start();
|
|
40123
40181
|
try {
|
|
40124
40182
|
const jiti = (0, import_jiti.createJiti)(import_meta.url);
|
|
40125
|
-
const configModule = await jiti.import((0, import_path.resolve)(configPath));
|
|
40126
|
-
const config3 = configModule.default;
|
|
40127
|
-
if (!config3?.organization) {
|
|
40128
|
-
spinner.fail('Invalid config: missing "organization" field');
|
|
40129
|
-
console.error(source_default.gray(` Config file: ${(0, import_path.resolve)(configPath)}`));
|
|
40130
|
-
console.error(source_default.gray(' Expected: export default { organization: "your-org-name" }'));
|
|
40131
|
-
throw new Error("Invalid config");
|
|
40132
|
-
}
|
|
40133
40183
|
const entryModule = await jiti.import((0, import_path.resolve)(entryPath));
|
|
40134
40184
|
const org = entryModule.default;
|
|
40135
40185
|
if (!org) {
|
|
@@ -40138,7 +40188,7 @@ function registerCheckCommand(program3) {
|
|
|
40138
40188
|
console.error(source_default.gray(" Expected: export default { workflows: [...], agents: [...] }"));
|
|
40139
40189
|
throw new Error("Invalid entry");
|
|
40140
40190
|
}
|
|
40141
|
-
new ResourceRegistry({
|
|
40191
|
+
new ResourceRegistry({ _check: org });
|
|
40142
40192
|
const workflowCount = org.workflows?.length ?? 0;
|
|
40143
40193
|
const agentCount = org.agents?.length ?? 0;
|
|
40144
40194
|
const totalCount = workflowCount + agentCount;
|
|
@@ -40189,27 +40239,73 @@ function resolveEnvironment() {
|
|
|
40189
40239
|
return process.env.NODE_ENV === "development" ? "development" : "production";
|
|
40190
40240
|
}
|
|
40191
40241
|
|
|
40242
|
+
// src/cli/api-client.ts
|
|
40243
|
+
function getApiKey() {
|
|
40244
|
+
const key = process.env.ELEVASIS_API_KEY;
|
|
40245
|
+
if (!key) {
|
|
40246
|
+
throw new Error(
|
|
40247
|
+
"ELEVASIS_API_KEY environment variable is required.\nSet it in your .env file: ELEVASIS_API_KEY=sk_..."
|
|
40248
|
+
);
|
|
40249
|
+
}
|
|
40250
|
+
return key;
|
|
40251
|
+
}
|
|
40252
|
+
async function apiGet(endpoint, apiUrl = resolveApiUrl()) {
|
|
40253
|
+
const response = await fetch(`${apiUrl}${endpoint}`, {
|
|
40254
|
+
headers: { Authorization: `Bearer ${getApiKey()}` }
|
|
40255
|
+
});
|
|
40256
|
+
if (!response.ok) {
|
|
40257
|
+
const errorText = await response.text();
|
|
40258
|
+
throw new Error(`API request failed (${response.status}): ${errorText}`);
|
|
40259
|
+
}
|
|
40260
|
+
if (response.status === 204) {
|
|
40261
|
+
return {};
|
|
40262
|
+
}
|
|
40263
|
+
return response.json();
|
|
40264
|
+
}
|
|
40265
|
+
async function apiPost(endpoint, body, apiUrl = resolveApiUrl()) {
|
|
40266
|
+
const response = await fetch(`${apiUrl}${endpoint}`, {
|
|
40267
|
+
method: "POST",
|
|
40268
|
+
headers: {
|
|
40269
|
+
Authorization: `Bearer ${getApiKey()}`,
|
|
40270
|
+
"Content-Type": "application/json"
|
|
40271
|
+
},
|
|
40272
|
+
body: JSON.stringify(body)
|
|
40273
|
+
});
|
|
40274
|
+
if (!response.ok) {
|
|
40275
|
+
const errorText = await response.text();
|
|
40276
|
+
throw new Error(`API request failed (${response.status}): ${errorText}`);
|
|
40277
|
+
}
|
|
40278
|
+
if (response.status === 204) {
|
|
40279
|
+
return {};
|
|
40280
|
+
}
|
|
40281
|
+
return response.json();
|
|
40282
|
+
}
|
|
40283
|
+
|
|
40192
40284
|
// src/cli/commands/deploy.ts
|
|
40193
40285
|
var import_meta2 = {};
|
|
40194
40286
|
function registerDeployCommand(program3) {
|
|
40195
|
-
program3.command("deploy").description("Validate, bundle, upload, and deploy project resources\n Example: elevasis deploy --api-url http://localhost:5170").option("--api-url <url>", "API URL").option("--
|
|
40287
|
+
program3.command("deploy").description("Validate, bundle, upload, and deploy project resources\n Example: elevasis deploy --api-url http://localhost:5170").option("--api-url <url>", "API URL").option("--entry <path>", "Path to entry file (default: ./src/index.ts)").action(wrapAction("deploy", async (options) => {
|
|
40196
40288
|
const startTime = Date.now();
|
|
40197
40289
|
const apiUrl = resolveApiUrl(options.apiUrl);
|
|
40198
40290
|
const env2 = resolveEnvironment();
|
|
40199
|
-
const configPath = options.config ?? "./elevasis.config.ts";
|
|
40200
40291
|
const entryPath = options.entry ?? "./src/index.ts";
|
|
40292
|
+
const authSpinner = ora("Authenticating...").start();
|
|
40293
|
+
let orgName;
|
|
40294
|
+
try {
|
|
40295
|
+
const me = await apiGet("/api/external/me", apiUrl);
|
|
40296
|
+
orgName = me.organizationName;
|
|
40297
|
+
authSpinner.succeed(
|
|
40298
|
+
source_default.green("Authenticating...") + source_default.white(" done") + source_default.gray(` (${orgName})`)
|
|
40299
|
+
);
|
|
40300
|
+
} catch (error46) {
|
|
40301
|
+
authSpinner.fail(source_default.red("Authentication failed"));
|
|
40302
|
+
console.error(source_default.gray(" Check your ELEVASIS_API_KEY and API URL."));
|
|
40303
|
+
throw error46;
|
|
40304
|
+
}
|
|
40201
40305
|
const validateSpinner = ora("Validating...").start();
|
|
40202
|
-
let config3;
|
|
40203
40306
|
let org;
|
|
40204
40307
|
try {
|
|
40205
40308
|
const jiti = (0, import_jiti2.createJiti)(import_meta2.url);
|
|
40206
|
-
const configModule = await jiti.import((0, import_path2.resolve)(configPath));
|
|
40207
|
-
config3 = configModule.default;
|
|
40208
|
-
if (!config3?.organization) {
|
|
40209
|
-
validateSpinner.fail('Invalid config: missing "organization" field');
|
|
40210
|
-
console.error(source_default.gray(` Config file: ${(0, import_path2.resolve)(configPath)}`));
|
|
40211
|
-
throw new Error('Invalid config: missing "organization" field');
|
|
40212
|
-
}
|
|
40213
40309
|
const entryModule = await jiti.import((0, import_path2.resolve)(entryPath));
|
|
40214
40310
|
org = entryModule.default;
|
|
40215
40311
|
if (!org) {
|
|
@@ -40217,7 +40313,7 @@ function registerDeployCommand(program3) {
|
|
|
40217
40313
|
console.error(source_default.gray(` Entry file: ${(0, import_path2.resolve)(entryPath)}`));
|
|
40218
40314
|
throw new Error("Invalid entry: no default export found");
|
|
40219
40315
|
}
|
|
40220
|
-
new ResourceRegistry({ [
|
|
40316
|
+
new ResourceRegistry({ [orgName]: org });
|
|
40221
40317
|
const workflowCount = org.workflows?.length ?? 0;
|
|
40222
40318
|
const agentCount = org.agents?.length ?? 0;
|
|
40223
40319
|
const totalCount = workflowCount + agentCount;
|
|
@@ -40225,7 +40321,7 @@ function registerDeployCommand(program3) {
|
|
|
40225
40321
|
source_default.green("Validating...") + source_default.white(" done") + source_default.gray(` (${totalCount} resource${totalCount !== 1 ? "s" : ""}, 0 errors)`)
|
|
40226
40322
|
);
|
|
40227
40323
|
console.log("");
|
|
40228
|
-
console.log(source_default.gray(` Org: ${
|
|
40324
|
+
console.log(source_default.gray(` Org: ${orgName}`));
|
|
40229
40325
|
console.log(source_default.gray(` Target: ${apiUrl} (${env2})`));
|
|
40230
40326
|
console.log("");
|
|
40231
40327
|
for (const w of org.workflows ?? []) {
|
|
@@ -40401,48 +40497,6 @@ startWorker(org)
|
|
|
40401
40497
|
}));
|
|
40402
40498
|
}
|
|
40403
40499
|
|
|
40404
|
-
// src/cli/api-client.ts
|
|
40405
|
-
function getApiKey() {
|
|
40406
|
-
const key = process.env.ELEVASIS_API_KEY;
|
|
40407
|
-
if (!key) {
|
|
40408
|
-
throw new Error(
|
|
40409
|
-
"ELEVASIS_API_KEY environment variable is required.\nSet it in your .env file: ELEVASIS_API_KEY=sk_..."
|
|
40410
|
-
);
|
|
40411
|
-
}
|
|
40412
|
-
return key;
|
|
40413
|
-
}
|
|
40414
|
-
async function apiGet(endpoint, apiUrl = resolveApiUrl()) {
|
|
40415
|
-
const response = await fetch(`${apiUrl}${endpoint}`, {
|
|
40416
|
-
headers: { Authorization: `Bearer ${getApiKey()}` }
|
|
40417
|
-
});
|
|
40418
|
-
if (!response.ok) {
|
|
40419
|
-
const errorText = await response.text();
|
|
40420
|
-
throw new Error(`API request failed (${response.status}): ${errorText}`);
|
|
40421
|
-
}
|
|
40422
|
-
if (response.status === 204) {
|
|
40423
|
-
return {};
|
|
40424
|
-
}
|
|
40425
|
-
return response.json();
|
|
40426
|
-
}
|
|
40427
|
-
async function apiPost(endpoint, body, apiUrl = resolveApiUrl()) {
|
|
40428
|
-
const response = await fetch(`${apiUrl}${endpoint}`, {
|
|
40429
|
-
method: "POST",
|
|
40430
|
-
headers: {
|
|
40431
|
-
Authorization: `Bearer ${getApiKey()}`,
|
|
40432
|
-
"Content-Type": "application/json"
|
|
40433
|
-
},
|
|
40434
|
-
body: JSON.stringify(body)
|
|
40435
|
-
});
|
|
40436
|
-
if (!response.ok) {
|
|
40437
|
-
const errorText = await response.text();
|
|
40438
|
-
throw new Error(`API request failed (${response.status}): ${errorText}`);
|
|
40439
|
-
}
|
|
40440
|
-
if (response.status === 204) {
|
|
40441
|
-
return {};
|
|
40442
|
-
}
|
|
40443
|
-
return response.json();
|
|
40444
|
-
}
|
|
40445
|
-
|
|
40446
40500
|
// src/cli/commands/exec.ts
|
|
40447
40501
|
function registerExecCommand(program3) {
|
|
40448
40502
|
program3.command("exec <resourceId>").description(`Execute a deployed resource
|
|
@@ -40775,7 +40829,7 @@ var import_path3 = require("path");
|
|
|
40775
40829
|
var import_promises2 = require("fs/promises");
|
|
40776
40830
|
|
|
40777
40831
|
// src/cli/version.ts
|
|
40778
|
-
var SDK_VERSION = "0.4.
|
|
40832
|
+
var SDK_VERSION = "0.4.1";
|
|
40779
40833
|
|
|
40780
40834
|
// src/cli/commands/init.ts
|
|
40781
40835
|
var SCAFFOLD_FILES = [
|
|
@@ -40812,7 +40866,7 @@ function registerInitCommand(program3) {
|
|
|
40812
40866
|
}
|
|
40813
40867
|
await (0, import_promises2.mkdir)((0, import_path3.resolve)(targetDir, "src"), { recursive: true });
|
|
40814
40868
|
const files = {
|
|
40815
|
-
"elevasis.config.ts": configTemplate(
|
|
40869
|
+
"elevasis.config.ts": configTemplate(),
|
|
40816
40870
|
"package.json": packageJsonTemplate(orgSlug),
|
|
40817
40871
|
"pnpm-workspace.yaml": pnpmWorkspaceTemplate(),
|
|
40818
40872
|
"tsconfig.json": tsconfigTemplate(),
|
|
@@ -40840,12 +40894,10 @@ function toSlug(name) {
|
|
|
40840
40894
|
const slug = name.toLowerCase().replace(/[^a-z0-9-]/g, "-").replace(/^[^a-z]+/, "").replace(/-+/g, "-").replace(/-$/, "");
|
|
40841
40895
|
return slug.length >= 3 ? slug : "my-project";
|
|
40842
40896
|
}
|
|
40843
|
-
function configTemplate(
|
|
40897
|
+
function configTemplate() {
|
|
40844
40898
|
return `import type { ElevasConfig } from '@elevasis/sdk'
|
|
40845
40899
|
|
|
40846
|
-
export default {
|
|
40847
|
-
organization: '${organization}',
|
|
40848
|
-
} satisfies ElevasConfig
|
|
40900
|
+
export default {} satisfies ElevasConfig
|
|
40849
40901
|
`;
|
|
40850
40902
|
}
|
|
40851
40903
|
function packageJsonTemplate(organization) {
|
|
@@ -40938,7 +40990,7 @@ elevasis executions <resourceId>
|
|
|
40938
40990
|
|
|
40939
40991
|
## Project Structure
|
|
40940
40992
|
|
|
40941
|
-
- \`elevasis.config.ts\` --
|
|
40993
|
+
- \`elevasis.config.ts\` -- Project config (optional settings)
|
|
40942
40994
|
- \`src/index.ts\` -- Resource definitions (workflows, agents)
|
|
40943
40995
|
- \`.env\` -- API key and environment variables
|
|
40944
40996
|
`;
|
package/dist/index.d.ts
CHANGED
|
@@ -1104,11 +1104,12 @@ declare class ResourceRegistry {
|
|
|
1104
1104
|
*/
|
|
1105
1105
|
private serializedCache;
|
|
1106
1106
|
/**
|
|
1107
|
-
*
|
|
1108
|
-
*
|
|
1109
|
-
*
|
|
1107
|
+
* Per-resource remote configuration (external deployments)
|
|
1108
|
+
* Key: "orgName/resourceId", Value: RemoteOrgConfig for that resource.
|
|
1109
|
+
* Tracks which individual resources were added at runtime via deploy pipeline.
|
|
1110
|
+
* Static and remote resources coexist in the same org.
|
|
1110
1111
|
*/
|
|
1111
|
-
private
|
|
1112
|
+
private remoteResources;
|
|
1112
1113
|
constructor(registry: OrganizationRegistry);
|
|
1113
1114
|
/**
|
|
1114
1115
|
* Validates registry on construction
|
|
@@ -1145,47 +1146,53 @@ declare class ResourceRegistry {
|
|
|
1145
1146
|
*/
|
|
1146
1147
|
listAllResources(): OrganizationRegistry;
|
|
1147
1148
|
/**
|
|
1148
|
-
* Register
|
|
1149
|
+
* Register external resources at runtime
|
|
1149
1150
|
*
|
|
1150
1151
|
* Called during deploy pipeline when an external developer deploys their bundle.
|
|
1151
|
-
*
|
|
1152
|
-
* for worker thread execution branching.
|
|
1152
|
+
* Merges the incoming stub definitions into the org's registry and stores
|
|
1153
|
+
* per-resource remote config for worker thread execution branching.
|
|
1153
1154
|
*
|
|
1154
|
-
*
|
|
1155
|
-
*
|
|
1155
|
+
* Static and remote resources coexist in the same org. If the org already
|
|
1156
|
+
* has static resources, the incoming remote resources are merged alongside them.
|
|
1157
|
+
* If redeploying (some resources already registered as remote for this org),
|
|
1158
|
+
* the previous remote resources are unregistered first.
|
|
1156
1159
|
*
|
|
1157
1160
|
* @param orgName - Organization name (used as registry key)
|
|
1158
1161
|
* @param org - Stub resource definitions (workflows/agents with placeholder handlers)
|
|
1159
1162
|
* @param remote - Remote configuration (bundle path, deployment ID, env vars)
|
|
1160
|
-
* @throws Error if
|
|
1163
|
+
* @throws Error if incoming resourceId conflicts with a static resource
|
|
1164
|
+
* @throws Error if incoming deployment contains duplicate resourceIds
|
|
1161
1165
|
*/
|
|
1162
1166
|
registerOrganization(orgName: string, org: OrganizationResources, remote: RemoteOrgConfig): void;
|
|
1163
1167
|
/**
|
|
1164
|
-
* Unregister
|
|
1168
|
+
* Unregister runtime-registered resources for an organization
|
|
1165
1169
|
*
|
|
1166
|
-
*
|
|
1167
|
-
* Static
|
|
1168
|
-
*
|
|
1170
|
+
* Removes only resources that were registered at runtime (via registerOrganization).
|
|
1171
|
+
* Static resources loaded at startup are preserved. If the org still has static
|
|
1172
|
+
* resources after removal, the serialization cache is rebuilt. If no resources
|
|
1173
|
+
* remain, the org is fully removed from the registry.
|
|
1174
|
+
* No-op if the org has no remote resources.
|
|
1169
1175
|
*
|
|
1170
|
-
* @param orgName - Organization name to unregister
|
|
1176
|
+
* @param orgName - Organization name to unregister remote resources from
|
|
1171
1177
|
*/
|
|
1172
1178
|
unregisterOrganization(orgName: string): void;
|
|
1173
1179
|
/**
|
|
1174
|
-
* Get remote configuration for
|
|
1180
|
+
* Get remote configuration for a specific resource
|
|
1175
1181
|
*
|
|
1176
|
-
* Returns the RemoteOrgConfig if the
|
|
1177
|
-
* or null if it's a static
|
|
1182
|
+
* Returns the RemoteOrgConfig if the resource was registered at runtime,
|
|
1183
|
+
* or null if it's a static resource or doesn't exist.
|
|
1178
1184
|
* Used by the execution coordinator to branch between local and worker execution.
|
|
1179
1185
|
*
|
|
1180
1186
|
* @param orgName - Organization name
|
|
1187
|
+
* @param resourceId - Resource ID
|
|
1181
1188
|
* @returns Remote config or null
|
|
1182
1189
|
*/
|
|
1183
|
-
getRemoteConfig(orgName: string): RemoteOrgConfig | null;
|
|
1190
|
+
getRemoteConfig(orgName: string, resourceId: string): RemoteOrgConfig | null;
|
|
1184
1191
|
/**
|
|
1185
|
-
* Check if an organization
|
|
1192
|
+
* Check if an organization has any remote (externally deployed) resources
|
|
1186
1193
|
*
|
|
1187
1194
|
* @param orgName - Organization name
|
|
1188
|
-
* @returns true if the org
|
|
1195
|
+
* @returns true if the org has at least one runtime-registered resource
|
|
1189
1196
|
*/
|
|
1190
1197
|
isRemote(orgName: string): boolean;
|
|
1191
1198
|
/**
|
|
@@ -1903,10 +1910,10 @@ declare class RegistryValidationError extends Error {
|
|
|
1903
1910
|
|
|
1904
1911
|
/**
|
|
1905
1912
|
* Project configuration for an external developer project.
|
|
1906
|
-
* Defined in
|
|
1913
|
+
* Defined in elevasis.config.ts at the project root.
|
|
1914
|
+
* Organization is derived from the ELEVASIS_API_KEY -- not configured here.
|
|
1907
1915
|
*/
|
|
1908
1916
|
interface ElevasConfig {
|
|
1909
|
-
organization: string;
|
|
1910
1917
|
defaultStatus?: ResourceStatus;
|
|
1911
1918
|
dev?: {
|
|
1912
1919
|
port?: number;
|
package/dist/index.js
CHANGED
|
@@ -3166,11 +3166,12 @@ var ResourceRegistry = class {
|
|
|
3166
3166
|
*/
|
|
3167
3167
|
serializedCache;
|
|
3168
3168
|
/**
|
|
3169
|
-
*
|
|
3170
|
-
*
|
|
3171
|
-
*
|
|
3169
|
+
* Per-resource remote configuration (external deployments)
|
|
3170
|
+
* Key: "orgName/resourceId", Value: RemoteOrgConfig for that resource.
|
|
3171
|
+
* Tracks which individual resources were added at runtime via deploy pipeline.
|
|
3172
|
+
* Static and remote resources coexist in the same org.
|
|
3172
3173
|
*/
|
|
3173
|
-
|
|
3174
|
+
remoteResources = /* @__PURE__ */ new Map();
|
|
3174
3175
|
/**
|
|
3175
3176
|
* Validates registry on construction
|
|
3176
3177
|
* - Checks for duplicate resourceIds within organizations
|
|
@@ -3273,67 +3274,125 @@ var ResourceRegistry = class {
|
|
|
3273
3274
|
// Runtime Organization Registration (External Deployments)
|
|
3274
3275
|
// ============================================================================
|
|
3275
3276
|
/**
|
|
3276
|
-
* Register
|
|
3277
|
+
* Register external resources at runtime
|
|
3277
3278
|
*
|
|
3278
3279
|
* Called during deploy pipeline when an external developer deploys their bundle.
|
|
3279
|
-
*
|
|
3280
|
-
* for worker thread execution branching.
|
|
3280
|
+
* Merges the incoming stub definitions into the org's registry and stores
|
|
3281
|
+
* per-resource remote config for worker thread execution branching.
|
|
3281
3282
|
*
|
|
3282
|
-
*
|
|
3283
|
-
*
|
|
3283
|
+
* Static and remote resources coexist in the same org. If the org already
|
|
3284
|
+
* has static resources, the incoming remote resources are merged alongside them.
|
|
3285
|
+
* If redeploying (some resources already registered as remote for this org),
|
|
3286
|
+
* the previous remote resources are unregistered first.
|
|
3284
3287
|
*
|
|
3285
3288
|
* @param orgName - Organization name (used as registry key)
|
|
3286
3289
|
* @param org - Stub resource definitions (workflows/agents with placeholder handlers)
|
|
3287
3290
|
* @param remote - Remote configuration (bundle path, deployment ID, env vars)
|
|
3288
|
-
* @throws Error if
|
|
3291
|
+
* @throws Error if incoming resourceId conflicts with a static resource
|
|
3292
|
+
* @throws Error if incoming deployment contains duplicate resourceIds
|
|
3289
3293
|
*/
|
|
3290
3294
|
registerOrganization(orgName, org, remote) {
|
|
3291
|
-
|
|
3292
|
-
|
|
3295
|
+
const incomingWorkflowIds = (org.workflows ?? []).map((w) => w.config.resourceId);
|
|
3296
|
+
const incomingAgentIds = (org.agents ?? []).map((a) => a.config.resourceId);
|
|
3297
|
+
const incomingIds = [...incomingWorkflowIds, ...incomingAgentIds];
|
|
3298
|
+
const seen = /* @__PURE__ */ new Set();
|
|
3299
|
+
for (const id of incomingIds) {
|
|
3300
|
+
if (seen.has(id)) {
|
|
3301
|
+
throw new Error(
|
|
3302
|
+
`Duplicate resource ID '${id}' in deployment. Each resource must have a unique ID.`
|
|
3303
|
+
);
|
|
3304
|
+
}
|
|
3305
|
+
seen.add(id);
|
|
3293
3306
|
}
|
|
3294
|
-
if (this.
|
|
3307
|
+
if (this.isRemote(orgName)) {
|
|
3295
3308
|
this.unregisterOrganization(orgName);
|
|
3296
3309
|
}
|
|
3297
|
-
this.registry[orgName]
|
|
3298
|
-
|
|
3299
|
-
|
|
3310
|
+
const existingOrg = this.registry[orgName];
|
|
3311
|
+
if (existingOrg) {
|
|
3312
|
+
const staticWorkflowIds = new Set((existingOrg.workflows ?? []).map((w) => w.config.resourceId));
|
|
3313
|
+
const staticAgentIds = new Set((existingOrg.agents ?? []).map((a) => a.config.resourceId));
|
|
3314
|
+
for (const id of incomingIds) {
|
|
3315
|
+
if (staticWorkflowIds.has(id) || staticAgentIds.has(id)) {
|
|
3316
|
+
throw new Error(
|
|
3317
|
+
`Resource '${id}' already exists in '${orgName}' as an internal resource. External deployments cannot override internal resources.`
|
|
3318
|
+
);
|
|
3319
|
+
}
|
|
3320
|
+
}
|
|
3321
|
+
}
|
|
3322
|
+
if (existingOrg) {
|
|
3323
|
+
existingOrg.workflows = [...existingOrg.workflows ?? [], ...org.workflows ?? []];
|
|
3324
|
+
existingOrg.agents = [...existingOrg.agents ?? [], ...org.agents ?? []];
|
|
3325
|
+
} else {
|
|
3326
|
+
this.registry[orgName] = org;
|
|
3327
|
+
}
|
|
3328
|
+
for (const id of incomingIds) {
|
|
3329
|
+
this.remoteResources.set(`${orgName}/${id}`, remote);
|
|
3330
|
+
}
|
|
3331
|
+
this.serializedCache.set(orgName, serializeOrganization(this.registry[orgName]));
|
|
3300
3332
|
}
|
|
3301
3333
|
/**
|
|
3302
|
-
* Unregister
|
|
3334
|
+
* Unregister runtime-registered resources for an organization
|
|
3303
3335
|
*
|
|
3304
|
-
*
|
|
3305
|
-
* Static
|
|
3306
|
-
*
|
|
3336
|
+
* Removes only resources that were registered at runtime (via registerOrganization).
|
|
3337
|
+
* Static resources loaded at startup are preserved. If the org still has static
|
|
3338
|
+
* resources after removal, the serialization cache is rebuilt. If no resources
|
|
3339
|
+
* remain, the org is fully removed from the registry.
|
|
3340
|
+
* No-op if the org has no remote resources.
|
|
3307
3341
|
*
|
|
3308
|
-
* @param orgName - Organization name to unregister
|
|
3342
|
+
* @param orgName - Organization name to unregister remote resources from
|
|
3309
3343
|
*/
|
|
3310
3344
|
unregisterOrganization(orgName) {
|
|
3311
|
-
|
|
3312
|
-
|
|
3313
|
-
this.
|
|
3314
|
-
|
|
3345
|
+
const prefix = `${orgName}/`;
|
|
3346
|
+
const remoteIds = /* @__PURE__ */ new Set();
|
|
3347
|
+
for (const key of this.remoteResources.keys()) {
|
|
3348
|
+
if (key.startsWith(prefix)) {
|
|
3349
|
+
remoteIds.add(key.slice(prefix.length));
|
|
3350
|
+
this.remoteResources.delete(key);
|
|
3351
|
+
}
|
|
3352
|
+
}
|
|
3353
|
+
if (remoteIds.size === 0) return;
|
|
3354
|
+
const orgResources = this.registry[orgName];
|
|
3355
|
+
if (!orgResources) return;
|
|
3356
|
+
orgResources.workflows = (orgResources.workflows ?? []).filter(
|
|
3357
|
+
(w) => !remoteIds.has(w.config.resourceId)
|
|
3358
|
+
);
|
|
3359
|
+
orgResources.agents = (orgResources.agents ?? []).filter(
|
|
3360
|
+
(a) => !remoteIds.has(a.config.resourceId)
|
|
3361
|
+
);
|
|
3362
|
+
const remaining = (orgResources.workflows?.length ?? 0) + (orgResources.agents?.length ?? 0) + (orgResources.triggers?.length ?? 0) + (orgResources.integrations?.length ?? 0) + (orgResources.externalResources?.length ?? 0) + (orgResources.humanCheckpoints?.length ?? 0);
|
|
3363
|
+
if (remaining > 0) {
|
|
3364
|
+
this.serializedCache.set(orgName, serializeOrganization(orgResources));
|
|
3365
|
+
} else {
|
|
3366
|
+
delete this.registry[orgName];
|
|
3367
|
+
this.serializedCache.delete(orgName);
|
|
3368
|
+
}
|
|
3315
3369
|
}
|
|
3316
3370
|
/**
|
|
3317
|
-
* Get remote configuration for
|
|
3371
|
+
* Get remote configuration for a specific resource
|
|
3318
3372
|
*
|
|
3319
|
-
* Returns the RemoteOrgConfig if the
|
|
3320
|
-
* or null if it's a static
|
|
3373
|
+
* Returns the RemoteOrgConfig if the resource was registered at runtime,
|
|
3374
|
+
* or null if it's a static resource or doesn't exist.
|
|
3321
3375
|
* Used by the execution coordinator to branch between local and worker execution.
|
|
3322
3376
|
*
|
|
3323
3377
|
* @param orgName - Organization name
|
|
3378
|
+
* @param resourceId - Resource ID
|
|
3324
3379
|
* @returns Remote config or null
|
|
3325
3380
|
*/
|
|
3326
|
-
getRemoteConfig(orgName) {
|
|
3327
|
-
return this.
|
|
3381
|
+
getRemoteConfig(orgName, resourceId) {
|
|
3382
|
+
return this.remoteResources.get(`${orgName}/${resourceId}`) ?? null;
|
|
3328
3383
|
}
|
|
3329
3384
|
/**
|
|
3330
|
-
* Check if an organization
|
|
3385
|
+
* Check if an organization has any remote (externally deployed) resources
|
|
3331
3386
|
*
|
|
3332
3387
|
* @param orgName - Organization name
|
|
3333
|
-
* @returns true if the org
|
|
3388
|
+
* @returns true if the org has at least one runtime-registered resource
|
|
3334
3389
|
*/
|
|
3335
3390
|
isRemote(orgName) {
|
|
3336
|
-
|
|
3391
|
+
const prefix = `${orgName}/`;
|
|
3392
|
+
for (const key of this.remoteResources.keys()) {
|
|
3393
|
+
if (key.startsWith(prefix)) return true;
|
|
3394
|
+
}
|
|
3395
|
+
return false;
|
|
3337
3396
|
}
|
|
3338
3397
|
// ============================================================================
|
|
3339
3398
|
// Resource Manifest Accessors
|
package/dist/worker/index.js
CHANGED
|
@@ -64,8 +64,12 @@ function startWorker(org) {
|
|
|
64
64
|
const agents = new Map(
|
|
65
65
|
(org.agents ?? []).map((a) => [a.config.resourceId, a])
|
|
66
66
|
);
|
|
67
|
+
console.log(`[SDK-WORKER] Worker started with ${workflows.size} workflow(s), ${agents.size} agent(s)`);
|
|
67
68
|
parentPort.on("message", async (msg) => {
|
|
68
69
|
if (msg.type === "manifest") {
|
|
70
|
+
const workflowList = (org.workflows ?? []).map((w) => w.config.resourceId);
|
|
71
|
+
const agentList = (org.agents ?? []).map((a) => a.config.resourceId);
|
|
72
|
+
console.log(`[SDK-WORKER] Manifest requested: workflows=[${workflowList.join(", ")}], agents=[${agentList.join(", ")}]`);
|
|
69
73
|
parentPort.postMessage({
|
|
70
74
|
type: "manifest",
|
|
71
75
|
workflows: (org.workflows ?? []).map((w) => ({
|
|
@@ -88,18 +92,24 @@ function startWorker(org) {
|
|
|
88
92
|
return;
|
|
89
93
|
}
|
|
90
94
|
if (msg.type === "execute") {
|
|
91
|
-
const { resourceId, input } = msg;
|
|
95
|
+
const { resourceId, executionId, input } = msg;
|
|
96
|
+
console.log(`[SDK-WORKER] Execute request: resourceId=${resourceId}, executionId=${executionId}`);
|
|
92
97
|
const workflow = workflows.get(resourceId);
|
|
93
98
|
if (workflow) {
|
|
94
99
|
try {
|
|
100
|
+
console.log(`[SDK-WORKER] Running workflow '${resourceId}' (${Object.keys(workflow.steps).length} steps)`);
|
|
101
|
+
const startTime = Date.now();
|
|
95
102
|
const { output, logs } = await executeWorkflow(workflow, input);
|
|
103
|
+
console.log(`[SDK-WORKER] Workflow '${resourceId}' completed (${Date.now() - startTime}ms)`);
|
|
96
104
|
parentPort.postMessage({ type: "result", status: "completed", output, logs });
|
|
97
105
|
} catch (err) {
|
|
106
|
+
console.error(`[SDK-WORKER] Workflow '${resourceId}' failed: ${String(err)}`);
|
|
98
107
|
parentPort.postMessage({ type: "result", status: "failed", error: String(err), logs: [] });
|
|
99
108
|
}
|
|
100
109
|
return;
|
|
101
110
|
}
|
|
102
111
|
if (agents.has(resourceId)) {
|
|
112
|
+
console.error(`[SDK-WORKER] Agent execution not supported: ${resourceId}`);
|
|
103
113
|
parentPort.postMessage({
|
|
104
114
|
type: "result",
|
|
105
115
|
status: "failed",
|
|
@@ -108,6 +118,7 @@ function startWorker(org) {
|
|
|
108
118
|
});
|
|
109
119
|
return;
|
|
110
120
|
}
|
|
121
|
+
console.error(`[SDK-WORKER] Resource not found: ${resourceId}`);
|
|
111
122
|
parentPort.postMessage({
|
|
112
123
|
type: "result",
|
|
113
124
|
status: "failed",
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@elevasis/sdk",
|
|
3
|
-
"version": "0.4.
|
|
3
|
+
"version": "0.4.1",
|
|
4
4
|
"description": "SDK for building Elevasis organization resources",
|
|
5
5
|
"comment:bin": "IMPORTANT: This package shares the 'elevasis' binary name with @repo/cli. They never conflict because @elevasis/sdk must NEVER be added as a dependency of any workspace package (apps/*, packages/*, organizations/*). Workspace projects use @repo/cli for the 'elevasis' binary. External developers (outside the workspace) get this SDK's binary via npm install.",
|
|
6
6
|
"type": "module",
|