@kaelen-ai/cli 0.1.14 → 0.1.16
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/index.js +2149 -514
- package/dist/index.js.map +1 -1
- package/package.json +2 -2
package/dist/index.js
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
2
|
import { Command } from 'commander';
|
|
3
|
-
import
|
|
3
|
+
import chalk12 from 'chalk';
|
|
4
4
|
import { writeFile, readFile, mkdtemp, rm, mkdir, stat, readdir } from 'fs/promises';
|
|
5
5
|
import { join, resolve, dirname, relative, basename, extname, isAbsolute } from 'path';
|
|
6
6
|
import * as acorn from 'acorn';
|
|
@@ -9,16 +9,16 @@ import { transform, build } from 'esbuild';
|
|
|
9
9
|
import { spawn } from 'child_process';
|
|
10
10
|
import { createRequire } from 'module';
|
|
11
11
|
import { URL as URL$1, pathToFileURL } from 'url';
|
|
12
|
+
import { randomBytes, createHash } from 'crypto';
|
|
12
13
|
import { tmpdir, homedir } from 'os';
|
|
13
14
|
import { createWriteStream } from 'fs';
|
|
14
15
|
import archiver from 'archiver';
|
|
15
16
|
import { createInterface } from 'readline';
|
|
16
|
-
import open from 'open';
|
|
17
|
-
import { createServer } from 'http';
|
|
18
|
-
import { randomBytes, createHash } from 'crypto';
|
|
19
17
|
import WebSocket from 'ws';
|
|
20
18
|
import { Socket } from 'phoenix';
|
|
21
19
|
import * as AbsintheSocket from '@absinthe/socket';
|
|
20
|
+
import open from 'open';
|
|
21
|
+
import { createServer } from 'http';
|
|
22
22
|
|
|
23
23
|
// src/errors.ts
|
|
24
24
|
var CliError = class extends Error {
|
|
@@ -159,7 +159,14 @@ function validChannelPattern(value) {
|
|
|
159
159
|
|
|
160
160
|
// src/build/parser.ts
|
|
161
161
|
var SDK_PACKAGES = /* @__PURE__ */ new Set(["@io/sdk", "@kaelen-ai/sdk", "@eleven-am/sdk"]);
|
|
162
|
-
var EXECUTION_KEYS = /* @__PURE__ */ new Set([
|
|
162
|
+
var EXECUTION_KEYS = /* @__PURE__ */ new Set([
|
|
163
|
+
"memory",
|
|
164
|
+
"timeout",
|
|
165
|
+
"maxToolCalls",
|
|
166
|
+
"rateBudget",
|
|
167
|
+
"retries",
|
|
168
|
+
"concurrency"
|
|
169
|
+
]);
|
|
163
170
|
function loc(node, file) {
|
|
164
171
|
const position = node.loc?.start;
|
|
165
172
|
return {
|
|
@@ -311,7 +318,7 @@ function extractOnField(propValue, file, sdkImports) {
|
|
|
311
318
|
loc(propValue, file)
|
|
312
319
|
);
|
|
313
320
|
}
|
|
314
|
-
function extractExecutionConfig(properties) {
|
|
321
|
+
function extractExecutionConfig(properties, file) {
|
|
315
322
|
const config = {};
|
|
316
323
|
for (const prop of properties) {
|
|
317
324
|
if (prop.type === "SpreadElement") {
|
|
@@ -321,10 +328,7 @@ function extractExecutionConfig(properties) {
|
|
|
321
328
|
if (!keyName || !EXECUTION_KEYS.has(keyName)) {
|
|
322
329
|
continue;
|
|
323
330
|
}
|
|
324
|
-
|
|
325
|
-
if (value.type === "Literal") {
|
|
326
|
-
config[keyName] = value.value;
|
|
327
|
-
}
|
|
331
|
+
config[keyName] = extractStaticValue(prop.value, file);
|
|
328
332
|
}
|
|
329
333
|
return Object.keys(config).length > 0 ? config : void 0;
|
|
330
334
|
}
|
|
@@ -364,7 +368,6 @@ function parseBehaviorCall(node, args, ancestors, file, out, sdkImports) {
|
|
|
364
368
|
const properties = spec.properties;
|
|
365
369
|
let onRaw;
|
|
366
370
|
let hasRun = false;
|
|
367
|
-
let messageHistory;
|
|
368
371
|
for (const prop of properties) {
|
|
369
372
|
if (prop.type === "SpreadElement") {
|
|
370
373
|
continue;
|
|
@@ -377,23 +380,6 @@ function parseBehaviorCall(node, args, ancestors, file, out, sdkImports) {
|
|
|
377
380
|
onRaw = extractOnField(prop.value, file, sdkImports);
|
|
378
381
|
} else if (keyName === "run") {
|
|
379
382
|
hasRun = true;
|
|
380
|
-
} else if (keyName === "trackingId") {
|
|
381
|
-
const value = prop.value;
|
|
382
|
-
if (value.type !== "ArrowFunctionExpression" && value.type !== "FunctionExpression") {
|
|
383
|
-
throw new BuildError(
|
|
384
|
-
`behavior '${name}' trackingId must be a function in ${file}:${location.line}`,
|
|
385
|
-
location
|
|
386
|
-
);
|
|
387
|
-
}
|
|
388
|
-
} else if (keyName === "messageHistory") {
|
|
389
|
-
const value = prop.value;
|
|
390
|
-
if (value.type !== "Literal" || typeof value.value !== "number" || !Number.isInteger(value.value) || value.value <= 0) {
|
|
391
|
-
throw new BuildError(
|
|
392
|
-
`behavior '${name}' messageHistory must be a positive integer literal in ${file}:${location.line}`,
|
|
393
|
-
location
|
|
394
|
-
);
|
|
395
|
-
}
|
|
396
|
-
messageHistory = value.value;
|
|
397
383
|
}
|
|
398
384
|
}
|
|
399
385
|
if (!onRaw) {
|
|
@@ -402,14 +388,13 @@ function parseBehaviorCall(node, args, ancestors, file, out, sdkImports) {
|
|
|
402
388
|
if (!hasRun) {
|
|
403
389
|
throw new BuildError(`behavior '${name}' is missing a run function`, location);
|
|
404
390
|
}
|
|
405
|
-
const execution = extractExecutionConfig(properties);
|
|
391
|
+
const execution = extractExecutionConfig(properties, file);
|
|
406
392
|
const bounds = findStatementBounds(ancestors);
|
|
407
393
|
out.push({
|
|
408
394
|
name,
|
|
409
395
|
onRaw,
|
|
410
396
|
on: "",
|
|
411
397
|
execution,
|
|
412
|
-
messageHistory,
|
|
413
398
|
location,
|
|
414
399
|
statementStart: bounds.start,
|
|
415
400
|
statementEnd: bounds.end
|
|
@@ -490,7 +475,7 @@ function parseConversationCall(node, args, ancestors, file, out) {
|
|
|
490
475
|
location
|
|
491
476
|
);
|
|
492
477
|
}
|
|
493
|
-
const execution = extractExecutionConfig(properties);
|
|
478
|
+
const execution = extractExecutionConfig(properties, file);
|
|
494
479
|
const bounds = findStatementBounds(ancestors);
|
|
495
480
|
out.push({
|
|
496
481
|
name,
|
|
@@ -565,11 +550,7 @@ var MEMORY_EVENT_MAP = {
|
|
|
565
550
|
onTensionEmerged: "knowledge:tension.emerged",
|
|
566
551
|
onTensionEscalated: "knowledge:tension.escalated",
|
|
567
552
|
onTrajectoryDeteriorated: "knowledge:trajectory.deteriorated",
|
|
568
|
-
onRoutineBroken: "knowledge:routine.broken"
|
|
569
|
-
onContradiction: "knowledge:contradiction",
|
|
570
|
-
onDeadlinePassed: "knowledge:deadline_passed",
|
|
571
|
-
onIntentionExpired: "knowledge:intention_expired",
|
|
572
|
-
onTemporalDueSoon: "knowledge:temporal_due_soon"
|
|
553
|
+
onRoutineBroken: "knowledge:routine.broken"
|
|
573
554
|
};
|
|
574
555
|
function resolveAll(files) {
|
|
575
556
|
const signalVarMap = /* @__PURE__ */ new Map();
|
|
@@ -854,6 +835,228 @@ function validateEndpointsSection(endpoints, errors) {
|
|
|
854
835
|
var RESERVED_BUNDLE_PREFIX = "__kaelen.";
|
|
855
836
|
var BINDING_SCOPES = /* @__PURE__ */ new Set(["project", "principal"]);
|
|
856
837
|
var CREDENTIAL_SOURCES = /* @__PURE__ */ new Set(["none", "platform", "secret_bundle"]);
|
|
838
|
+
var TARGET_EXECUTION_KEYS = /* @__PURE__ */ new Set([
|
|
839
|
+
"applications",
|
|
840
|
+
"endpoints",
|
|
841
|
+
"secret_names",
|
|
842
|
+
"timeout_ms",
|
|
843
|
+
"memory_mb",
|
|
844
|
+
"max_tool_calls",
|
|
845
|
+
"rate_budget"
|
|
846
|
+
]);
|
|
847
|
+
var SECRET_HEADER_NAMES = /* @__PURE__ */ new Set([
|
|
848
|
+
"authorization",
|
|
849
|
+
"proxy-authorization",
|
|
850
|
+
"proxy-authenticate",
|
|
851
|
+
"www-authenticate",
|
|
852
|
+
"cookie",
|
|
853
|
+
"set-cookie",
|
|
854
|
+
"x-api-key",
|
|
855
|
+
"x-auth-token",
|
|
856
|
+
"x-access-token",
|
|
857
|
+
"x-secret",
|
|
858
|
+
"x-secret-key",
|
|
859
|
+
"x-amz-security-token"
|
|
860
|
+
]);
|
|
861
|
+
var HTTP_LIKE_KINDS = /* @__PURE__ */ new Set(["http", "graphql", "httpApi", "graphqlApi"]);
|
|
862
|
+
var OP_RESOURCE_KINDS = /* @__PURE__ */ new Set(["httpApi", "graphqlApi"]);
|
|
863
|
+
function countPlaceholders(format) {
|
|
864
|
+
let count = 0;
|
|
865
|
+
let idx = 0;
|
|
866
|
+
while ((idx = format.indexOf("{}", idx)) !== -1) {
|
|
867
|
+
count++;
|
|
868
|
+
idx += 2;
|
|
869
|
+
}
|
|
870
|
+
return count;
|
|
871
|
+
}
|
|
872
|
+
function validateStaticHeaders(owner, headers, errors) {
|
|
873
|
+
if (headers === void 0) return;
|
|
874
|
+
if (!isRecord2(headers)) {
|
|
875
|
+
errors.push(`${owner} headers must be an object`);
|
|
876
|
+
return;
|
|
877
|
+
}
|
|
878
|
+
for (const name of Object.keys(headers)) {
|
|
879
|
+
if (SECRET_HEADER_NAMES.has(name.toLowerCase())) {
|
|
880
|
+
errors.push(
|
|
881
|
+
`${owner} headers entry "${name}" is secret-bearing; declare it via credentialHeaders instead`
|
|
882
|
+
);
|
|
883
|
+
}
|
|
884
|
+
}
|
|
885
|
+
}
|
|
886
|
+
function validateCredentialHeadersBlock(owner, declared, errors, secretBundleNames, resourceSecretBundle, secretBundleSlots) {
|
|
887
|
+
if (declared === void 0) return;
|
|
888
|
+
if (!Array.isArray(declared)) {
|
|
889
|
+
errors.push(`${owner} credentialHeaders must be an array`);
|
|
890
|
+
return;
|
|
891
|
+
}
|
|
892
|
+
for (const entry of declared) {
|
|
893
|
+
if (!isRecord2(entry)) {
|
|
894
|
+
errors.push(`${owner} credentialHeaders entry must be an object`);
|
|
895
|
+
continue;
|
|
896
|
+
}
|
|
897
|
+
const name = stringField2(entry, "name");
|
|
898
|
+
if (!name) {
|
|
899
|
+
errors.push(`${owner} credentialHeaders entry is missing a string name`);
|
|
900
|
+
continue;
|
|
901
|
+
}
|
|
902
|
+
if (SECRET_HEADER_NAMES.has(name.toLowerCase())) ;
|
|
903
|
+
const source = entry.source;
|
|
904
|
+
if (!isRecord2(source)) {
|
|
905
|
+
errors.push(
|
|
906
|
+
`${owner} credentialHeaders "${name}" source must be an object`
|
|
907
|
+
);
|
|
908
|
+
continue;
|
|
909
|
+
}
|
|
910
|
+
const kind = stringField2(source, "kind");
|
|
911
|
+
if (kind === "credential") {
|
|
912
|
+
const field = stringField2(source, "field");
|
|
913
|
+
if (!field) {
|
|
914
|
+
errors.push(
|
|
915
|
+
`${owner} credentialHeaders "${name}" credential source missing field`
|
|
916
|
+
);
|
|
917
|
+
continue;
|
|
918
|
+
}
|
|
919
|
+
if (resourceSecretBundle === void 0) {
|
|
920
|
+
errors.push(
|
|
921
|
+
`${owner} credentialHeaders "${name}" uses credential source but resource declares no auth/secret_bundle`
|
|
922
|
+
);
|
|
923
|
+
} else if (!resourceSecretBundle.startsWith(RESERVED_BUNDLE_PREFIX)) {
|
|
924
|
+
const slots = secretBundleSlots.get(resourceSecretBundle);
|
|
925
|
+
if (slots && !slots.has(field)) {
|
|
926
|
+
errors.push(
|
|
927
|
+
`${owner} credentialHeaders "${name}" credential field "${field}" is not declared in secret bundle "${resourceSecretBundle}"`
|
|
928
|
+
);
|
|
929
|
+
}
|
|
930
|
+
}
|
|
931
|
+
} else if (kind === "rawSecret" || kind === "raw_secret") {
|
|
932
|
+
const secretName = stringField2(source, "name");
|
|
933
|
+
if (!secretName) {
|
|
934
|
+
errors.push(
|
|
935
|
+
`${owner} credentialHeaders "${name}" raw_secret source missing name`
|
|
936
|
+
);
|
|
937
|
+
continue;
|
|
938
|
+
}
|
|
939
|
+
if (!secretName.startsWith(RESERVED_BUNDLE_PREFIX) && !secretBundleNames.has(secretName)) {
|
|
940
|
+
errors.push(
|
|
941
|
+
`${owner} credentialHeaders "${name}" references unknown raw_secret "${secretName}"`
|
|
942
|
+
);
|
|
943
|
+
}
|
|
944
|
+
} else {
|
|
945
|
+
errors.push(
|
|
946
|
+
`${owner} credentialHeaders "${name}" source.kind must be credential or rawSecret`
|
|
947
|
+
);
|
|
948
|
+
continue;
|
|
949
|
+
}
|
|
950
|
+
const format = entry.format;
|
|
951
|
+
if (format !== void 0) {
|
|
952
|
+
if (typeof format !== "string" || countPlaceholders(format) !== 1) {
|
|
953
|
+
errors.push(
|
|
954
|
+
`${owner} credentialHeaders "${name}" format must contain exactly one {} placeholder`
|
|
955
|
+
);
|
|
956
|
+
}
|
|
957
|
+
}
|
|
958
|
+
}
|
|
959
|
+
}
|
|
960
|
+
function validateRequestMappingBlock(owner, declared, errors) {
|
|
961
|
+
if (declared === void 0) return;
|
|
962
|
+
if (!isRecord2(declared)) {
|
|
963
|
+
errors.push(`${owner} requestMapping must be an object`);
|
|
964
|
+
return;
|
|
965
|
+
}
|
|
966
|
+
for (const key of ["pathParams", "query"]) {
|
|
967
|
+
const value = declared[key];
|
|
968
|
+
if (value === void 0) continue;
|
|
969
|
+
if (!Array.isArray(value)) {
|
|
970
|
+
errors.push(`${owner} requestMapping.${key} must be an array`);
|
|
971
|
+
continue;
|
|
972
|
+
}
|
|
973
|
+
for (const entry of value) {
|
|
974
|
+
if (typeof entry === "string") {
|
|
975
|
+
if (!entry.trim()) {
|
|
976
|
+
errors.push(
|
|
977
|
+
`${owner} requestMapping.${key} entries must be non-empty strings or {from, as?} objects`
|
|
978
|
+
);
|
|
979
|
+
}
|
|
980
|
+
continue;
|
|
981
|
+
}
|
|
982
|
+
if (isRecord2(entry)) {
|
|
983
|
+
if (!stringField2(entry, "from")) {
|
|
984
|
+
errors.push(
|
|
985
|
+
`${owner} requestMapping.${key} entry.from must be a non-empty string`
|
|
986
|
+
);
|
|
987
|
+
continue;
|
|
988
|
+
}
|
|
989
|
+
if (entry.as !== void 0 && (typeof entry.as !== "string" || !entry.as.trim())) {
|
|
990
|
+
errors.push(
|
|
991
|
+
`${owner} requestMapping.${key} entry.as must be a non-empty string when provided`
|
|
992
|
+
);
|
|
993
|
+
}
|
|
994
|
+
continue;
|
|
995
|
+
}
|
|
996
|
+
errors.push(
|
|
997
|
+
`${owner} requestMapping.${key} entries must be strings or {from, as?} objects`
|
|
998
|
+
);
|
|
999
|
+
}
|
|
1000
|
+
}
|
|
1001
|
+
if (declared.body !== void 0 && typeof declared.body !== "string" && declared.body !== null) {
|
|
1002
|
+
errors.push(`${owner} requestMapping.body must be a string or null`);
|
|
1003
|
+
}
|
|
1004
|
+
}
|
|
1005
|
+
function validateHttpLikeConfig(appName, resourceName, resourceKind, config, secretBundleNames, resourceSecretBundle, secretBundleSlots, errors) {
|
|
1006
|
+
if (!HTTP_LIKE_KINDS.has(resourceKind)) return;
|
|
1007
|
+
if (!isRecord2(config)) return;
|
|
1008
|
+
const base = `resource "${appName}/${resourceName}"`;
|
|
1009
|
+
validateStaticHeaders(base, config.headers, errors);
|
|
1010
|
+
validateCredentialHeadersBlock(
|
|
1011
|
+
base,
|
|
1012
|
+
config.credentialHeaders,
|
|
1013
|
+
errors,
|
|
1014
|
+
secretBundleNames,
|
|
1015
|
+
resourceSecretBundle,
|
|
1016
|
+
secretBundleSlots
|
|
1017
|
+
);
|
|
1018
|
+
if (resourceKind === "http") {
|
|
1019
|
+
validateRequestMappingBlock(base, config.requestMapping, errors);
|
|
1020
|
+
}
|
|
1021
|
+
if (resourceKind === "graphql") {
|
|
1022
|
+
if (config.variablesMapping !== void 0 && typeof config.variablesMapping !== "string") {
|
|
1023
|
+
errors.push(`${base} variablesMapping must be a string`);
|
|
1024
|
+
}
|
|
1025
|
+
}
|
|
1026
|
+
if (OP_RESOURCE_KINDS.has(resourceKind)) {
|
|
1027
|
+
const ops = config.operations;
|
|
1028
|
+
if (ops !== void 0 && !isRecord2(ops)) {
|
|
1029
|
+
errors.push(`${base} operations must be an object`);
|
|
1030
|
+
return;
|
|
1031
|
+
}
|
|
1032
|
+
if (isRecord2(ops)) {
|
|
1033
|
+
for (const [opName, opSpec] of Object.entries(ops)) {
|
|
1034
|
+
const owner = `${base} operation "${opName}"`;
|
|
1035
|
+
if (!isRecord2(opSpec)) {
|
|
1036
|
+
errors.push(`${owner} must be an object`);
|
|
1037
|
+
continue;
|
|
1038
|
+
}
|
|
1039
|
+
validateStaticHeaders(owner, opSpec.headers, errors);
|
|
1040
|
+
validateCredentialHeadersBlock(
|
|
1041
|
+
owner,
|
|
1042
|
+
opSpec.credentialHeaders,
|
|
1043
|
+
errors,
|
|
1044
|
+
secretBundleNames,
|
|
1045
|
+
resourceSecretBundle,
|
|
1046
|
+
secretBundleSlots
|
|
1047
|
+
);
|
|
1048
|
+
if (resourceKind === "httpApi") {
|
|
1049
|
+
validateRequestMappingBlock(owner, opSpec.requestMapping, errors);
|
|
1050
|
+
}
|
|
1051
|
+
if (resourceKind === "graphqlApi") {
|
|
1052
|
+
if (opSpec.variablesMapping !== void 0 && typeof opSpec.variablesMapping !== "string") {
|
|
1053
|
+
errors.push(`${owner} variablesMapping must be a string`);
|
|
1054
|
+
}
|
|
1055
|
+
}
|
|
1056
|
+
}
|
|
1057
|
+
}
|
|
1058
|
+
}
|
|
1059
|
+
}
|
|
857
1060
|
function isRecord2(value) {
|
|
858
1061
|
return !!value && typeof value === "object" && !Array.isArray(value);
|
|
859
1062
|
}
|
|
@@ -865,12 +1068,6 @@ function stringField2(record, key) {
|
|
|
865
1068
|
const value = record[key];
|
|
866
1069
|
return typeof value === "string" && value.trim() ? value : void 0;
|
|
867
1070
|
}
|
|
868
|
-
function scopeListField(record, key) {
|
|
869
|
-
const value = record[key];
|
|
870
|
-
if (value === void 0) return void 0;
|
|
871
|
-
if (!Array.isArray(value)) return [];
|
|
872
|
-
return value.flatMap((entry) => scopeEntryName(entry) ?? []);
|
|
873
|
-
}
|
|
874
1071
|
function scopeEntryName(entry) {
|
|
875
1072
|
if (typeof entry === "string" && entry.trim()) return entry;
|
|
876
1073
|
if (isRecord2(entry)) return stringField2(entry, "name");
|
|
@@ -916,6 +1113,11 @@ function validateTargetExecution(target, declaredApps, declaredEndpoints, errors
|
|
|
916
1113
|
return;
|
|
917
1114
|
}
|
|
918
1115
|
const executionRecord = isRecord2(execution) ? execution : {};
|
|
1116
|
+
for (const key of Object.keys(executionRecord)) {
|
|
1117
|
+
if (!TARGET_EXECUTION_KEYS.has(key)) {
|
|
1118
|
+
errors.push(`${owner} execution contains unsupported key "${key}"`);
|
|
1119
|
+
}
|
|
1120
|
+
}
|
|
919
1121
|
const applicationRefs = validateScopeList(
|
|
920
1122
|
owner,
|
|
921
1123
|
"execution.applications",
|
|
@@ -938,31 +1140,60 @@ function validateTargetExecution(target, declaredApps, declaredEndpoints, errors
|
|
|
938
1140
|
errors.push(`${owner} references undeclared endpoint "${ref}"`);
|
|
939
1141
|
}
|
|
940
1142
|
}
|
|
941
|
-
|
|
942
|
-
|
|
943
|
-
|
|
944
|
-
|
|
945
|
-
|
|
946
|
-
|
|
947
|
-
|
|
948
|
-
|
|
1143
|
+
const secretRefs = validateScopeList(
|
|
1144
|
+
owner,
|
|
1145
|
+
"execution.secret_names",
|
|
1146
|
+
executionRecord.secret_names,
|
|
1147
|
+
errors
|
|
1148
|
+
);
|
|
1149
|
+
for (const ref of secretRefs) {
|
|
1150
|
+
if (ref.startsWith(RESERVED_BUNDLE_PREFIX)) {
|
|
1151
|
+
errors.push(
|
|
1152
|
+
`${owner} execution.secret_names entry "${ref}" uses the reserved "${RESERVED_BUNDLE_PREFIX}" prefix`
|
|
1153
|
+
);
|
|
949
1154
|
}
|
|
950
1155
|
}
|
|
951
|
-
|
|
952
|
-
|
|
953
|
-
|
|
954
|
-
|
|
955
|
-
|
|
956
|
-
|
|
957
|
-
|
|
958
|
-
|
|
959
|
-
|
|
960
|
-
|
|
961
|
-
|
|
962
|
-
|
|
963
|
-
|
|
964
|
-
|
|
1156
|
+
validatePositiveInteger(owner, "execution.timeout_ms", executionRecord.timeout_ms, errors);
|
|
1157
|
+
validatePositiveInteger(owner, "execution.memory_mb", executionRecord.memory_mb, errors);
|
|
1158
|
+
validateNonNegativeInteger(owner, "execution.max_tool_calls", executionRecord.max_tool_calls, errors);
|
|
1159
|
+
validateRateBudget(owner, executionRecord.rate_budget, errors);
|
|
1160
|
+
if (target.endpoints !== void 0) {
|
|
1161
|
+
errors.push(`${owner} endpoints is no longer supported; use execution.endpoints`);
|
|
1162
|
+
}
|
|
1163
|
+
}
|
|
1164
|
+
function validateRateBudget(owner, value, errors) {
|
|
1165
|
+
if (value === void 0) return;
|
|
1166
|
+
if (!isRecord2(value)) {
|
|
1167
|
+
errors.push(`${owner} execution.rate_budget must be an object`);
|
|
1168
|
+
return;
|
|
1169
|
+
}
|
|
1170
|
+
validateBudgetMap(owner, "execution.rate_budget.per_application", value.per_application, errors);
|
|
1171
|
+
validateBudgetMap(owner, "execution.rate_budget.per_capability", value.per_capability, errors);
|
|
1172
|
+
}
|
|
1173
|
+
function validateBudgetMap(owner, field, value, errors) {
|
|
1174
|
+
if (value === void 0) return;
|
|
1175
|
+
if (!isRecord2(value)) {
|
|
1176
|
+
errors.push(`${owner} ${field} must be an object`);
|
|
1177
|
+
return;
|
|
1178
|
+
}
|
|
1179
|
+
for (const [key, budget] of Object.entries(value)) {
|
|
1180
|
+
if (!key.trim()) {
|
|
1181
|
+
errors.push(`${owner} ${field} keys must be non-empty strings`);
|
|
1182
|
+
continue;
|
|
965
1183
|
}
|
|
1184
|
+
validateNonNegativeInteger(owner, `${field}.${key}`, budget, errors);
|
|
1185
|
+
}
|
|
1186
|
+
}
|
|
1187
|
+
function validatePositiveInteger(owner, field, value, errors) {
|
|
1188
|
+
if (value === void 0) return;
|
|
1189
|
+
if (!Number.isInteger(value) || value <= 0) {
|
|
1190
|
+
errors.push(`${owner} ${field} must be a positive integer`);
|
|
1191
|
+
}
|
|
1192
|
+
}
|
|
1193
|
+
function validateNonNegativeInteger(owner, field, value, errors) {
|
|
1194
|
+
if (value === void 0) return;
|
|
1195
|
+
if (!Number.isInteger(value) || value < 0) {
|
|
1196
|
+
errors.push(`${owner} ${field} must be a non-negative integer`);
|
|
966
1197
|
}
|
|
967
1198
|
}
|
|
968
1199
|
function capabilityId(applicationName, resourceName, toolName) {
|
|
@@ -1056,11 +1287,11 @@ function validateManifest(manifest) {
|
|
|
1056
1287
|
const root = isRecord2(manifest) ? manifest : {};
|
|
1057
1288
|
const errors = [];
|
|
1058
1289
|
const seenCapabilities = /* @__PURE__ */ new Set();
|
|
1059
|
-
if (root.contract_version !== "io.invocation.
|
|
1060
|
-
errors.push("contract_version must be io.invocation.
|
|
1290
|
+
if (root.contract_version !== "io.invocation.v2") {
|
|
1291
|
+
errors.push("contract_version must be io.invocation.v2");
|
|
1061
1292
|
}
|
|
1062
|
-
if (root.snapshot_version !== "io_runtime.snapshot.
|
|
1063
|
-
errors.push("snapshot_version must be io_runtime.snapshot.
|
|
1293
|
+
if (root.snapshot_version !== "io_runtime.snapshot.v2") {
|
|
1294
|
+
errors.push("snapshot_version must be io_runtime.snapshot.v2");
|
|
1064
1295
|
}
|
|
1065
1296
|
const secretBundles = arrayField(root, "secret_bundles").filter(isRecord2);
|
|
1066
1297
|
duplicateNames(secretBundles, "secret bundle", errors);
|
|
@@ -1075,6 +1306,14 @@ function validateManifest(manifest) {
|
|
|
1075
1306
|
const secretNames = new Set(
|
|
1076
1307
|
secretBundles.map((entry) => stringField2(entry, "name")).filter((name) => !!name)
|
|
1077
1308
|
);
|
|
1309
|
+
const secretBundleSlots = /* @__PURE__ */ new Map();
|
|
1310
|
+
for (const bundle of secretBundles) {
|
|
1311
|
+
const bundleName = stringField2(bundle, "name");
|
|
1312
|
+
if (!bundleName) continue;
|
|
1313
|
+
const slots = bundle.slots;
|
|
1314
|
+
if (!isRecord2(slots)) continue;
|
|
1315
|
+
secretBundleSlots.set(bundleName, new Set(Object.keys(slots)));
|
|
1316
|
+
}
|
|
1078
1317
|
const declaredEndpoints = validateEndpointsSection(
|
|
1079
1318
|
arrayField(root, "endpoints"),
|
|
1080
1319
|
errors
|
|
@@ -1121,6 +1360,17 @@ function validateManifest(manifest) {
|
|
|
1121
1360
|
} else {
|
|
1122
1361
|
validateResourceCapability(appName, resourceName, capability, errors);
|
|
1123
1362
|
}
|
|
1363
|
+
const resourceKindForConfig = stringField2(resource, "kind") ?? "";
|
|
1364
|
+
validateHttpLikeConfig(
|
|
1365
|
+
appName,
|
|
1366
|
+
resourceName,
|
|
1367
|
+
resourceKindForConfig,
|
|
1368
|
+
resource.config,
|
|
1369
|
+
secretNames,
|
|
1370
|
+
secretBundle,
|
|
1371
|
+
secretBundleSlots,
|
|
1372
|
+
errors
|
|
1373
|
+
);
|
|
1124
1374
|
const tools = toolEntries.filter(isRecord2);
|
|
1125
1375
|
duplicateNames(tools, `resource "${appName}/${resourceName}" tool`, errors);
|
|
1126
1376
|
for (const tool of tools) {
|
|
@@ -1588,6 +1838,12 @@ var RUNTIME_MEMORY_SIGNALS = {
|
|
|
1588
1838
|
onTrajectoryDeteriorated: "knowledge:trajectory.deteriorated",
|
|
1589
1839
|
onRoutineBroken: "knowledge:routine.broken"
|
|
1590
1840
|
};
|
|
1841
|
+
var RUNTIME_SDK_PASSTHROUGH_EXPORTS = /* @__PURE__ */ new Set([
|
|
1842
|
+
"google",
|
|
1843
|
+
"microsoft",
|
|
1844
|
+
"httpApi",
|
|
1845
|
+
"graphqlApi"
|
|
1846
|
+
]);
|
|
1591
1847
|
var RUNTIME_SDK_EXPORTS = /* @__PURE__ */ new Set([
|
|
1592
1848
|
"ioSchema",
|
|
1593
1849
|
"zodSchema",
|
|
@@ -1608,7 +1864,8 @@ var RUNTIME_SDK_EXPORTS = /* @__PURE__ */ new Set([
|
|
|
1608
1864
|
"graphql",
|
|
1609
1865
|
"http",
|
|
1610
1866
|
"calendar",
|
|
1611
|
-
"contacts"
|
|
1867
|
+
"contacts",
|
|
1868
|
+
...RUNTIME_SDK_PASSTHROUGH_EXPORTS
|
|
1612
1869
|
]);
|
|
1613
1870
|
var COMPILED_AWAY_EXPORTS = /* @__PURE__ */ new Set(["behavior", "conversation"]);
|
|
1614
1871
|
function normalizeSdkSpecifier(specifier) {
|
|
@@ -1870,6 +2127,29 @@ ${entries.map(([key, value]) => ` ${key}: Object.freeze({ name: ${JSON.stringif
|
|
|
1870
2127
|
}`
|
|
1871
2128
|
);
|
|
1872
2129
|
}
|
|
2130
|
+
const needsPassthroughHelper = [...RUNTIME_SDK_PASSTHROUGH_EXPORTS].some(
|
|
2131
|
+
(name) => usage.exports.has(name)
|
|
2132
|
+
);
|
|
2133
|
+
if (needsPassthroughHelper) {
|
|
2134
|
+
lines.push(
|
|
2135
|
+
`function passthroughFrozen(options) {
|
|
2136
|
+
if (options && typeof options === "object" && !Array.isArray(options)) {
|
|
2137
|
+
return Object.freeze({ ...options });
|
|
2138
|
+
}
|
|
2139
|
+
return Object.freeze({});
|
|
2140
|
+
}`
|
|
2141
|
+
);
|
|
2142
|
+
for (const passthrough of RUNTIME_SDK_PASSTHROUGH_EXPORTS) {
|
|
2143
|
+
if (!usage.exports.has(passthrough)) {
|
|
2144
|
+
continue;
|
|
2145
|
+
}
|
|
2146
|
+
lines.push(
|
|
2147
|
+
`export function ${passthrough}(options) {
|
|
2148
|
+
return passthroughFrozen(options);
|
|
2149
|
+
}`
|
|
2150
|
+
);
|
|
2151
|
+
}
|
|
2152
|
+
}
|
|
1873
2153
|
return lines.join("\n\n");
|
|
1874
2154
|
}
|
|
1875
2155
|
function sdkAliasPlugin() {
|
|
@@ -2208,65 +2488,514 @@ async function generateManifest(input) {
|
|
|
2208
2488
|
);
|
|
2209
2489
|
return m;
|
|
2210
2490
|
}
|
|
2211
|
-
|
|
2212
|
-
|
|
2213
|
-
|
|
2214
|
-
|
|
2215
|
-
var INTERNAL_ON_MESSAGE = "__io_cli_compiled_on_message";
|
|
2216
|
-
var INTERNAL_ON_END = "__io_cli_compiled_on_end";
|
|
2217
|
-
var INTERNAL_APPLICATIONS = "__io_cli_compiled_applications";
|
|
2218
|
-
function conversationChannel(name) {
|
|
2219
|
-
return `session:${name}`;
|
|
2220
|
-
}
|
|
2221
|
-
function parseModule2(source) {
|
|
2222
|
-
return acorn.parse(source, {
|
|
2223
|
-
ecmaVersion: "latest",
|
|
2224
|
-
sourceType: "module"
|
|
2225
|
-
});
|
|
2491
|
+
|
|
2492
|
+
// src/build/stable-json.ts
|
|
2493
|
+
function stableStringify(value, indent) {
|
|
2494
|
+
return JSON.stringify(canonicalize(value), null, indent);
|
|
2226
2495
|
}
|
|
2227
|
-
function
|
|
2228
|
-
|
|
2229
|
-
|
|
2230
|
-
|
|
2231
|
-
} catch (error) {
|
|
2232
|
-
throw new BuildError(
|
|
2233
|
-
`failed to parse compiled target source: ${error instanceof Error ? error.message : String(error)}`
|
|
2234
|
-
);
|
|
2496
|
+
function canonicalize(value) {
|
|
2497
|
+
if (value === null) return null;
|
|
2498
|
+
if (Array.isArray(value)) {
|
|
2499
|
+
return value.map(canonicalize);
|
|
2235
2500
|
}
|
|
2236
|
-
|
|
2237
|
-
|
|
2238
|
-
|
|
2239
|
-
|
|
2240
|
-
|
|
2241
|
-
|
|
2501
|
+
if (typeof value === "object") {
|
|
2502
|
+
const obj = value;
|
|
2503
|
+
const sorted = {};
|
|
2504
|
+
for (const key of Object.keys(obj).sort()) {
|
|
2505
|
+
sorted[key] = canonicalize(obj[key]);
|
|
2506
|
+
}
|
|
2507
|
+
return sorted;
|
|
2242
2508
|
}
|
|
2243
|
-
|
|
2244
|
-
|
|
2245
|
-
|
|
2509
|
+
return value;
|
|
2510
|
+
}
|
|
2511
|
+
|
|
2512
|
+
// src/build/catalog.ts
|
|
2513
|
+
var CATALOG_CONTRACT_VERSION = "io.invocation.v2";
|
|
2514
|
+
var CATALOG_SNAPSHOT_VERSION = "io_runtime.snapshot.v2";
|
|
2515
|
+
var WEBHOOK_RESOURCE_KIND = "webhook";
|
|
2516
|
+
var PLATFORM_TOOL_KINDS = /* @__PURE__ */ new Set(["email", "calendar", "contacts"]);
|
|
2517
|
+
var STREAM_PUBLISH_KINDS = /* @__PURE__ */ new Set([
|
|
2518
|
+
"kafka",
|
|
2519
|
+
"rabbitmq",
|
|
2520
|
+
"redis",
|
|
2521
|
+
"pubsub",
|
|
2522
|
+
"sqs",
|
|
2523
|
+
"websocket"
|
|
2524
|
+
]);
|
|
2525
|
+
function sha256Hex(value) {
|
|
2526
|
+
return `sha256:${createHash("sha256").update(stableStringify(value)).digest("hex")}`;
|
|
2527
|
+
}
|
|
2528
|
+
function isRecord3(value) {
|
|
2529
|
+
return !!value && typeof value === "object" && !Array.isArray(value);
|
|
2530
|
+
}
|
|
2531
|
+
function getString(record, key) {
|
|
2532
|
+
const value = record[key];
|
|
2533
|
+
return typeof value === "string" ? value : void 0;
|
|
2534
|
+
}
|
|
2535
|
+
function getRecord(record, key) {
|
|
2536
|
+
const value = record[key];
|
|
2537
|
+
return isRecord3(value) ? value : void 0;
|
|
2538
|
+
}
|
|
2539
|
+
function getArray(record, key) {
|
|
2540
|
+
const value = record[key];
|
|
2541
|
+
return Array.isArray(value) ? value : [];
|
|
2542
|
+
}
|
|
2543
|
+
function getNumber(record, key) {
|
|
2544
|
+
const value = record[key];
|
|
2545
|
+
return typeof value === "number" && Number.isFinite(value) ? value : void 0;
|
|
2546
|
+
}
|
|
2547
|
+
function countPlaceholders2(format) {
|
|
2548
|
+
let count = 0;
|
|
2549
|
+
let idx = 0;
|
|
2550
|
+
while ((idx = format.indexOf("{}", idx)) !== -1) {
|
|
2551
|
+
count++;
|
|
2552
|
+
idx += 2;
|
|
2553
|
+
}
|
|
2554
|
+
return count;
|
|
2555
|
+
}
|
|
2556
|
+
function deriveAuditRequirement(effect) {
|
|
2557
|
+
return effect === "write" ? "pre_call" : "finish_only";
|
|
2558
|
+
}
|
|
2559
|
+
function deriveEffect(toolMode) {
|
|
2560
|
+
return toolMode === "read" ? "read" : "write";
|
|
2561
|
+
}
|
|
2562
|
+
function normalizeCredentialHeaders(declared, resourceId, context, warnings) {
|
|
2563
|
+
if (!Array.isArray(declared)) return [];
|
|
2564
|
+
const out = [];
|
|
2565
|
+
for (const entry of declared) {
|
|
2566
|
+
if (!isRecord3(entry)) {
|
|
2567
|
+
warnings.push(`${context}: skipped non-object credential header entry`);
|
|
2568
|
+
continue;
|
|
2246
2569
|
}
|
|
2247
|
-
const
|
|
2248
|
-
if (
|
|
2249
|
-
|
|
2570
|
+
const name = getString(entry, "name");
|
|
2571
|
+
if (!name) {
|
|
2572
|
+
warnings.push(`${context}: credential header missing name`);
|
|
2573
|
+
continue;
|
|
2250
2574
|
}
|
|
2251
|
-
const
|
|
2252
|
-
if (
|
|
2253
|
-
|
|
2575
|
+
const source = getRecord(entry, "source");
|
|
2576
|
+
if (!source) {
|
|
2577
|
+
warnings.push(`${context}: credential header "${name}" missing source`);
|
|
2578
|
+
continue;
|
|
2254
2579
|
}
|
|
2255
|
-
const
|
|
2256
|
-
|
|
2257
|
-
|
|
2580
|
+
const sourceKind = getString(source, "kind");
|
|
2581
|
+
let normalizedSource;
|
|
2582
|
+
if (sourceKind === "credential") {
|
|
2583
|
+
const field = getString(source, "field");
|
|
2584
|
+
if (!field) {
|
|
2585
|
+
warnings.push(
|
|
2586
|
+
`${context}: credential header "${name}" credential source missing field`
|
|
2587
|
+
);
|
|
2588
|
+
continue;
|
|
2589
|
+
}
|
|
2590
|
+
normalizedSource = { kind: "credential", resource_id: resourceId, field };
|
|
2591
|
+
} else if (sourceKind === "rawSecret" || sourceKind === "raw_secret") {
|
|
2592
|
+
const secretName = getString(source, "name");
|
|
2593
|
+
if (!secretName) {
|
|
2594
|
+
warnings.push(
|
|
2595
|
+
`${context}: credential header "${name}" raw_secret source missing name`
|
|
2596
|
+
);
|
|
2597
|
+
continue;
|
|
2598
|
+
}
|
|
2599
|
+
normalizedSource = { kind: "raw_secret", name: secretName };
|
|
2600
|
+
} else {
|
|
2601
|
+
warnings.push(
|
|
2602
|
+
`${context}: credential header "${name}" has unsupported source kind "${sourceKind}"`
|
|
2603
|
+
);
|
|
2604
|
+
continue;
|
|
2258
2605
|
}
|
|
2259
|
-
|
|
2260
|
-
|
|
2261
|
-
|
|
2262
|
-
|
|
2263
|
-
|
|
2264
|
-
|
|
2606
|
+
const format = getString(entry, "format") ?? "{}";
|
|
2607
|
+
if (countPlaceholders2(format) !== 1) {
|
|
2608
|
+
warnings.push(
|
|
2609
|
+
`${context}: credential header "${name}" format must contain exactly one {} placeholder`
|
|
2610
|
+
);
|
|
2611
|
+
continue;
|
|
2612
|
+
}
|
|
2613
|
+
out.push({ name, source: normalizedSource, format });
|
|
2265
2614
|
}
|
|
2266
|
-
return
|
|
2615
|
+
return out;
|
|
2267
2616
|
}
|
|
2268
|
-
function
|
|
2269
|
-
if (
|
|
2617
|
+
function normalizeRequestMapping(declared) {
|
|
2618
|
+
if (!isRecord3(declared)) {
|
|
2619
|
+
return { path_params: [], query: [] };
|
|
2620
|
+
}
|
|
2621
|
+
const pathParams = collectMappingValues(declared["pathParams"]);
|
|
2622
|
+
const query = collectMappingValues(declared["query"]);
|
|
2623
|
+
const body = declared["body"];
|
|
2624
|
+
const bodyValue = typeof body === "string" ? body : body === null ? null : void 0;
|
|
2625
|
+
return {
|
|
2626
|
+
path_params: pathParams,
|
|
2627
|
+
query,
|
|
2628
|
+
...bodyValue !== void 0 ? { body: bodyValue } : {}
|
|
2629
|
+
};
|
|
2630
|
+
}
|
|
2631
|
+
function collectMappingValues(value) {
|
|
2632
|
+
if (!Array.isArray(value)) return [];
|
|
2633
|
+
const out = [];
|
|
2634
|
+
for (const entry of value) {
|
|
2635
|
+
if (typeof entry === "string" && entry.trim()) {
|
|
2636
|
+
out.push(entry);
|
|
2637
|
+
} else if (isRecord3(entry)) {
|
|
2638
|
+
const from = getString(entry, "from");
|
|
2639
|
+
if (!from?.trim()) continue;
|
|
2640
|
+
const alias = getString(entry, "as");
|
|
2641
|
+
out.push(alias?.trim() ? { from, as: alias } : from);
|
|
2642
|
+
}
|
|
2643
|
+
}
|
|
2644
|
+
return out;
|
|
2645
|
+
}
|
|
2646
|
+
function buildExecutor(ctx) {
|
|
2647
|
+
if (PLATFORM_TOOL_KINDS.has(ctx.resourceKind)) {
|
|
2648
|
+
return {
|
|
2649
|
+
kind: "platform_tool",
|
|
2650
|
+
provider_kind: ctx.providerKind,
|
|
2651
|
+
backend_kind: ctx.backendKind,
|
|
2652
|
+
operation: ctx.toolName
|
|
2653
|
+
};
|
|
2654
|
+
}
|
|
2655
|
+
if (ctx.resourceKind === WEBHOOK_RESOURCE_KIND && ctx.toolName === "respond") {
|
|
2656
|
+
return { kind: "webhook_response" };
|
|
2657
|
+
}
|
|
2658
|
+
if (STREAM_PUBLISH_KINDS.has(ctx.resourceKind)) {
|
|
2659
|
+
return {
|
|
2660
|
+
kind: "stream_publish",
|
|
2661
|
+
provider_kind: ctx.providerKind,
|
|
2662
|
+
backend_kind: ctx.backendKind,
|
|
2663
|
+
destination_ref: ctx.configHash
|
|
2664
|
+
};
|
|
2665
|
+
}
|
|
2666
|
+
if (ctx.resourceKind === "http") {
|
|
2667
|
+
return buildHttpRequestExecutor(ctx);
|
|
2668
|
+
}
|
|
2669
|
+
if (ctx.resourceKind === "graphql") {
|
|
2670
|
+
return buildGraphQlRequestExecutor(ctx);
|
|
2671
|
+
}
|
|
2672
|
+
if (ctx.resourceKind === "httpApi") {
|
|
2673
|
+
return buildHttpOperationExecutor(ctx);
|
|
2674
|
+
}
|
|
2675
|
+
if (ctx.resourceKind === "graphqlApi") {
|
|
2676
|
+
return buildGraphQlOperationExecutor(ctx);
|
|
2677
|
+
}
|
|
2678
|
+
return {
|
|
2679
|
+
kind: "unsupported",
|
|
2680
|
+
reason: `unknown_resource_kind:${ctx.resourceKind}`
|
|
2681
|
+
};
|
|
2682
|
+
}
|
|
2683
|
+
function buildHttpRequestExecutor(ctx) {
|
|
2684
|
+
const config = ctx.resourceConfig;
|
|
2685
|
+
const url = getString(config, "url") ?? "";
|
|
2686
|
+
const method = (getString(config, "method") ?? "GET").toUpperCase();
|
|
2687
|
+
const headers = getRecord(config, "headers") ?? {};
|
|
2688
|
+
const queryParams = getRecord(config, "queryParams") ?? {};
|
|
2689
|
+
const body = config["body"];
|
|
2690
|
+
const credentialHeaders = normalizeCredentialHeaders(
|
|
2691
|
+
config["credentialHeaders"],
|
|
2692
|
+
ctx.resourceId,
|
|
2693
|
+
`tool "${ctx.resourceId}.${ctx.toolName}"`,
|
|
2694
|
+
ctx.warnings
|
|
2695
|
+
);
|
|
2696
|
+
return {
|
|
2697
|
+
kind: "http_request",
|
|
2698
|
+
url,
|
|
2699
|
+
method,
|
|
2700
|
+
headers_ref: ctx.registerBlob(headers),
|
|
2701
|
+
credential_headers: credentialHeaders,
|
|
2702
|
+
query_params_ref: ctx.registerBlob(queryParams),
|
|
2703
|
+
body_ref: ctx.registerBlob(body ?? null),
|
|
2704
|
+
timeout_seconds: getNumber(config, "timeoutSeconds") ?? null
|
|
2705
|
+
};
|
|
2706
|
+
}
|
|
2707
|
+
function buildGraphQlRequestExecutor(ctx) {
|
|
2708
|
+
const config = ctx.resourceConfig;
|
|
2709
|
+
const endpoint = getString(config, "endpoint") ?? "";
|
|
2710
|
+
const query = getString(config, "query") ?? "";
|
|
2711
|
+
const headers = getRecord(config, "headers") ?? {};
|
|
2712
|
+
const variables = getRecord(config, "variables") ?? {};
|
|
2713
|
+
const operationName = getString(config, "operationName") ?? null;
|
|
2714
|
+
const credentialHeaders = normalizeCredentialHeaders(
|
|
2715
|
+
config["credentialHeaders"],
|
|
2716
|
+
ctx.resourceId,
|
|
2717
|
+
`tool "${ctx.resourceId}.${ctx.toolName}"`,
|
|
2718
|
+
ctx.warnings
|
|
2719
|
+
);
|
|
2720
|
+
return {
|
|
2721
|
+
kind: "graphql_request",
|
|
2722
|
+
endpoint,
|
|
2723
|
+
query_ref: ctx.registerBlob(query),
|
|
2724
|
+
operation_name: operationName,
|
|
2725
|
+
variables_ref: ctx.registerBlob(variables),
|
|
2726
|
+
headers_ref: ctx.registerBlob(headers),
|
|
2727
|
+
credential_headers: credentialHeaders,
|
|
2728
|
+
timeout_seconds: getNumber(config, "timeoutSeconds") ?? null
|
|
2729
|
+
};
|
|
2730
|
+
}
|
|
2731
|
+
function buildHttpOperationExecutor(ctx) {
|
|
2732
|
+
const config = ctx.resourceConfig;
|
|
2733
|
+
const ops = getRecord(config, "operations") ?? {};
|
|
2734
|
+
const opSpec = getRecord(ops, ctx.toolName);
|
|
2735
|
+
if (!opSpec) {
|
|
2736
|
+
ctx.warnings.push(
|
|
2737
|
+
`tool "${ctx.resourceId}.${ctx.toolName}" has no matching operation in resource config`
|
|
2738
|
+
);
|
|
2739
|
+
return {
|
|
2740
|
+
kind: "unsupported",
|
|
2741
|
+
reason: `httpApi_missing_op:${ctx.toolName}`
|
|
2742
|
+
};
|
|
2743
|
+
}
|
|
2744
|
+
const baseUrl = getString(config, "baseUrl") ?? "";
|
|
2745
|
+
const resourceHeaders = getRecord(config, "headers") ?? {};
|
|
2746
|
+
const opHeaders = getRecord(opSpec, "headers") ?? {};
|
|
2747
|
+
const mergedHeaders = { ...resourceHeaders, ...opHeaders };
|
|
2748
|
+
const opCredentialHeaders = opSpec["credentialHeaders"];
|
|
2749
|
+
const resourceCredentialHeaders = config["credentialHeaders"];
|
|
2750
|
+
const credentialHeaders = normalizeCredentialHeaders(
|
|
2751
|
+
opCredentialHeaders ?? resourceCredentialHeaders,
|
|
2752
|
+
ctx.resourceId,
|
|
2753
|
+
`tool "${ctx.resourceId}.${ctx.toolName}"`,
|
|
2754
|
+
ctx.warnings
|
|
2755
|
+
);
|
|
2756
|
+
const method = (getString(opSpec, "method") ?? "GET").toUpperCase();
|
|
2757
|
+
const path = getString(opSpec, "path") ?? "";
|
|
2758
|
+
const timeoutSeconds = getNumber(opSpec, "timeoutSeconds") ?? getNumber(config, "timeoutSeconds") ?? null;
|
|
2759
|
+
return {
|
|
2760
|
+
kind: "http_operation",
|
|
2761
|
+
base_url: baseUrl,
|
|
2762
|
+
method,
|
|
2763
|
+
path,
|
|
2764
|
+
headers_ref: ctx.registerBlob(mergedHeaders),
|
|
2765
|
+
credential_headers: credentialHeaders,
|
|
2766
|
+
timeout_seconds: timeoutSeconds,
|
|
2767
|
+
request_mapping: normalizeRequestMapping(opSpec["requestMapping"])
|
|
2768
|
+
};
|
|
2769
|
+
}
|
|
2770
|
+
function buildGraphQlOperationExecutor(ctx) {
|
|
2771
|
+
const config = ctx.resourceConfig;
|
|
2772
|
+
const ops = getRecord(config, "operations") ?? {};
|
|
2773
|
+
const opSpec = getRecord(ops, ctx.toolName);
|
|
2774
|
+
if (!opSpec) {
|
|
2775
|
+
ctx.warnings.push(
|
|
2776
|
+
`tool "${ctx.resourceId}.${ctx.toolName}" has no matching operation in resource config`
|
|
2777
|
+
);
|
|
2778
|
+
return {
|
|
2779
|
+
kind: "unsupported",
|
|
2780
|
+
reason: `graphqlApi_missing_op:${ctx.toolName}`
|
|
2781
|
+
};
|
|
2782
|
+
}
|
|
2783
|
+
const endpoint = getString(config, "endpoint") ?? "";
|
|
2784
|
+
const resourceHeaders = getRecord(config, "headers") ?? {};
|
|
2785
|
+
const opHeaders = getRecord(opSpec, "headers") ?? {};
|
|
2786
|
+
const mergedHeaders = { ...resourceHeaders, ...opHeaders };
|
|
2787
|
+
const opCredentialHeaders = opSpec["credentialHeaders"];
|
|
2788
|
+
const resourceCredentialHeaders = config["credentialHeaders"];
|
|
2789
|
+
const credentialHeaders = normalizeCredentialHeaders(
|
|
2790
|
+
opCredentialHeaders ?? resourceCredentialHeaders,
|
|
2791
|
+
ctx.resourceId,
|
|
2792
|
+
`tool "${ctx.resourceId}.${ctx.toolName}"`,
|
|
2793
|
+
ctx.warnings
|
|
2794
|
+
);
|
|
2795
|
+
const query = getString(opSpec, "query") ?? "";
|
|
2796
|
+
const operationName = getString(opSpec, "operationName") ?? null;
|
|
2797
|
+
const variablesMapping = getString(opSpec, "variablesMapping") ?? "input";
|
|
2798
|
+
const timeoutSeconds = getNumber(opSpec, "timeoutSeconds") ?? getNumber(config, "timeoutSeconds") ?? null;
|
|
2799
|
+
return {
|
|
2800
|
+
kind: "graphql_operation",
|
|
2801
|
+
endpoint,
|
|
2802
|
+
query_ref: ctx.registerBlob(query),
|
|
2803
|
+
operation_name: operationName,
|
|
2804
|
+
headers_ref: ctx.registerBlob(mergedHeaders),
|
|
2805
|
+
credential_headers: credentialHeaders,
|
|
2806
|
+
timeout_seconds: timeoutSeconds,
|
|
2807
|
+
variables_mapping: variablesMapping
|
|
2808
|
+
};
|
|
2809
|
+
}
|
|
2810
|
+
function generateCatalog(manifest) {
|
|
2811
|
+
const warnings = [];
|
|
2812
|
+
const resources = {};
|
|
2813
|
+
const signals2 = {};
|
|
2814
|
+
const tools = {};
|
|
2815
|
+
const schemas = {};
|
|
2816
|
+
const registerBlob = (value) => {
|
|
2817
|
+
const hash = sha256Hex(value ?? {});
|
|
2818
|
+
if (!(hash in schemas)) {
|
|
2819
|
+
schemas[hash] = value ?? {};
|
|
2820
|
+
}
|
|
2821
|
+
return hash;
|
|
2822
|
+
};
|
|
2823
|
+
const apps = getArray(manifest, "apps").filter(isRecord3);
|
|
2824
|
+
for (const app of apps) {
|
|
2825
|
+
const appName = getString(app, "name");
|
|
2826
|
+
if (!appName) continue;
|
|
2827
|
+
const appResources2 = getArray(app, "resources").filter(isRecord3);
|
|
2828
|
+
for (const resource of appResources2) {
|
|
2829
|
+
const resourceName = getString(resource, "name");
|
|
2830
|
+
const resourceKind = getString(resource, "kind") ?? "";
|
|
2831
|
+
const backend = getString(resource, "backend") ?? "";
|
|
2832
|
+
const providerKind = getString(resource, "provider_kind") ?? resourceKind;
|
|
2833
|
+
if (!resourceName) continue;
|
|
2834
|
+
const resourceId = `${appName}.${resourceName}`;
|
|
2835
|
+
const config = getRecord(resource, "config") ?? {};
|
|
2836
|
+
const configHash = registerBlob(config);
|
|
2837
|
+
const capability = getRecord(resource, "capability") ?? {};
|
|
2838
|
+
const bindingScope = getString(capability, "binding_scope") ?? "project";
|
|
2839
|
+
const credentialSource = getString(capability, "credential_source") ?? "none";
|
|
2840
|
+
const requiresBindingValue = capability.requires_binding;
|
|
2841
|
+
const credentialExposure = requiresBindingValue === true ? "stdlib" : "none";
|
|
2842
|
+
const resourceSignals = getArray(resource, "signals").filter(isRecord3);
|
|
2843
|
+
const resourceTools2 = getArray(resource, "tools").filter(isRecord3);
|
|
2844
|
+
const emittedSignalIds = [];
|
|
2845
|
+
for (const signal of resourceSignals) {
|
|
2846
|
+
const signalName = getString(signal, "name");
|
|
2847
|
+
if (!signalName) continue;
|
|
2848
|
+
const signalId = `${resourceId}.${signalName}`;
|
|
2849
|
+
const signalScope = getString(signal, "scope") === "principal" ? "principal" : "project";
|
|
2850
|
+
const signalSchema = getRecord(signal, "schema") ?? {};
|
|
2851
|
+
const schemaRef = registerBlob(signalSchema);
|
|
2852
|
+
signals2[signalId] = {
|
|
2853
|
+
id: signalId,
|
|
2854
|
+
resource_id: resourceId,
|
|
2855
|
+
name: signalName,
|
|
2856
|
+
event_name: getString(signal, "event_name") ?? "",
|
|
2857
|
+
scope: signalScope,
|
|
2858
|
+
mode: getString(signal, "mode") ?? "",
|
|
2859
|
+
schema_ref: schemaRef
|
|
2860
|
+
};
|
|
2861
|
+
emittedSignalIds.push(signalId);
|
|
2862
|
+
}
|
|
2863
|
+
const emittedToolIds = [];
|
|
2864
|
+
for (const tool of resourceTools2) {
|
|
2865
|
+
const toolName = getString(tool, "name");
|
|
2866
|
+
if (!toolName) continue;
|
|
2867
|
+
const toolId = `${resourceId}.${toolName}`;
|
|
2868
|
+
const effect = deriveEffect(tool.mode);
|
|
2869
|
+
const toolCapability = getRecord(tool, "capability") ?? {};
|
|
2870
|
+
const toolBackendKind = getString(toolCapability, "backend_kind") ?? backend;
|
|
2871
|
+
const toolProviderKind = getString(toolCapability, "provider_kind") ?? providerKind;
|
|
2872
|
+
const toolCredentialExposure = getString(toolCapability, "credential_exposure") ?? "none";
|
|
2873
|
+
const argsSchemaRef = registerBlob(
|
|
2874
|
+
getRecord(tool, "args_schema") ?? {}
|
|
2875
|
+
);
|
|
2876
|
+
const resultSchemaRef = registerBlob(
|
|
2877
|
+
getRecord(tool, "result_schema") ?? {}
|
|
2878
|
+
);
|
|
2879
|
+
const executor = buildExecutor({
|
|
2880
|
+
resourceId,
|
|
2881
|
+
resourceKind,
|
|
2882
|
+
providerKind: toolProviderKind,
|
|
2883
|
+
backendKind: toolBackendKind,
|
|
2884
|
+
toolName,
|
|
2885
|
+
resourceConfig: config,
|
|
2886
|
+
configHash,
|
|
2887
|
+
registerBlob,
|
|
2888
|
+
warnings
|
|
2889
|
+
});
|
|
2890
|
+
if (executor.kind === "unsupported") {
|
|
2891
|
+
warnings.push(
|
|
2892
|
+
`tool "${toolId}" executor deferred: ${executor.reason}`
|
|
2893
|
+
);
|
|
2894
|
+
}
|
|
2895
|
+
tools[toolId] = {
|
|
2896
|
+
id: toolId,
|
|
2897
|
+
resource_id: resourceId,
|
|
2898
|
+
name: toolName,
|
|
2899
|
+
effect,
|
|
2900
|
+
audit_requirement: deriveAuditRequirement(effect),
|
|
2901
|
+
credential_exposure: toolCredentialExposure,
|
|
2902
|
+
args_schema_ref: argsSchemaRef,
|
|
2903
|
+
result_schema_ref: resultSchemaRef,
|
|
2904
|
+
executor
|
|
2905
|
+
};
|
|
2906
|
+
emittedToolIds.push(toolId);
|
|
2907
|
+
}
|
|
2908
|
+
resources[resourceId] = {
|
|
2909
|
+
id: resourceId,
|
|
2910
|
+
application_name: appName,
|
|
2911
|
+
resource_name: resourceName,
|
|
2912
|
+
resource_kind: resourceKind,
|
|
2913
|
+
provider_kind: providerKind,
|
|
2914
|
+
backend_kind: backend,
|
|
2915
|
+
binding_scope: bindingScope,
|
|
2916
|
+
credential_source: credentialSource,
|
|
2917
|
+
credential_exposure: credentialExposure,
|
|
2918
|
+
config_ref: configHash,
|
|
2919
|
+
signals: emittedSignalIds,
|
|
2920
|
+
tools: emittedToolIds
|
|
2921
|
+
};
|
|
2922
|
+
}
|
|
2923
|
+
}
|
|
2924
|
+
const catalogBody = {
|
|
2925
|
+
contract_version: CATALOG_CONTRACT_VERSION,
|
|
2926
|
+
snapshot_version: CATALOG_SNAPSHOT_VERSION,
|
|
2927
|
+
resources,
|
|
2928
|
+
signals: signals2,
|
|
2929
|
+
tools,
|
|
2930
|
+
schemas
|
|
2931
|
+
};
|
|
2932
|
+
const catalogHash = sha256Hex(catalogBody);
|
|
2933
|
+
return {
|
|
2934
|
+
catalog: {
|
|
2935
|
+
...catalogBody,
|
|
2936
|
+
catalog_hash: catalogHash
|
|
2937
|
+
},
|
|
2938
|
+
warnings
|
|
2939
|
+
};
|
|
2940
|
+
}
|
|
2941
|
+
var INTERNAL_CONFIG = "__io_cli_compiled_config";
|
|
2942
|
+
var INTERNAL_RUN = "__io_cli_compiled_run";
|
|
2943
|
+
var INTERNAL_ON_START = "__io_cli_compiled_on_start";
|
|
2944
|
+
var INTERNAL_ON_MESSAGE = "__io_cli_compiled_on_message";
|
|
2945
|
+
var INTERNAL_ON_END = "__io_cli_compiled_on_end";
|
|
2946
|
+
var INTERNAL_APPLICATIONS = "__io_cli_compiled_applications";
|
|
2947
|
+
function conversationChannel(name) {
|
|
2948
|
+
return `session:${name}`;
|
|
2949
|
+
}
|
|
2950
|
+
function parseModule2(source) {
|
|
2951
|
+
return acorn.parse(source, {
|
|
2952
|
+
ecmaVersion: "latest",
|
|
2953
|
+
sourceType: "module"
|
|
2954
|
+
});
|
|
2955
|
+
}
|
|
2956
|
+
function rootStatementFor(source, statementStart, kind, name) {
|
|
2957
|
+
let ast;
|
|
2958
|
+
try {
|
|
2959
|
+
ast = parseModule2(source);
|
|
2960
|
+
} catch (error) {
|
|
2961
|
+
throw new BuildError(
|
|
2962
|
+
`failed to parse compiled target source: ${error instanceof Error ? error.message : String(error)}`
|
|
2963
|
+
);
|
|
2964
|
+
}
|
|
2965
|
+
const body = ast.body;
|
|
2966
|
+
const stmt = body.find(
|
|
2967
|
+
(candidate) => candidate.start === statementStart
|
|
2968
|
+
);
|
|
2969
|
+
if (stmt) {
|
|
2970
|
+
return stmt;
|
|
2971
|
+
}
|
|
2972
|
+
const fallback = body.find((candidate) => {
|
|
2973
|
+
if (candidate.type !== "ExpressionStatement") {
|
|
2974
|
+
return false;
|
|
2975
|
+
}
|
|
2976
|
+
const expression = candidate.expression;
|
|
2977
|
+
if (expression.type !== "CallExpression") {
|
|
2978
|
+
return false;
|
|
2979
|
+
}
|
|
2980
|
+
const args = expression.arguments;
|
|
2981
|
+
if (args.length < 2 || args[0]?.type !== "Literal" || args[1]?.type !== "ObjectExpression") {
|
|
2982
|
+
return false;
|
|
2983
|
+
}
|
|
2984
|
+
const callee = expression.callee;
|
|
2985
|
+
if (callee.type !== "Identifier") {
|
|
2986
|
+
return false;
|
|
2987
|
+
}
|
|
2988
|
+
return callee.name === kind && args[0].value === name;
|
|
2989
|
+
});
|
|
2990
|
+
if (!fallback) {
|
|
2991
|
+
throw new BuildError(
|
|
2992
|
+
`failed to locate compiled ${kind} root '${name}' at byte offset ${statementStart}`
|
|
2993
|
+
);
|
|
2994
|
+
}
|
|
2995
|
+
return fallback;
|
|
2996
|
+
}
|
|
2997
|
+
function rootSpecNode(stmt) {
|
|
2998
|
+
if (stmt.type !== "ExpressionStatement") {
|
|
2270
2999
|
throw new BuildError("compiled target root must be an expression statement");
|
|
2271
3000
|
}
|
|
2272
3001
|
const expression = stmt.expression;
|
|
@@ -2567,13 +3296,90 @@ function pruneUnusedTopLevel(source, seeds) {
|
|
|
2567
3296
|
}
|
|
2568
3297
|
return applyEdits2(source, edits);
|
|
2569
3298
|
}
|
|
3299
|
+
function parseDurationMs(value) {
|
|
3300
|
+
if (value === void 0) return void 0;
|
|
3301
|
+
if (typeof value === "number") {
|
|
3302
|
+
if (Number.isInteger(value) && value > 0) return value;
|
|
3303
|
+
throw new BuildError("execution.timeout must be a positive integer number of milliseconds");
|
|
3304
|
+
}
|
|
3305
|
+
if (typeof value !== "string") {
|
|
3306
|
+
throw new BuildError("execution.timeout must be a duration string or milliseconds");
|
|
3307
|
+
}
|
|
3308
|
+
const match = value.trim().toLowerCase().match(/^(\d+)\s*(ms|s|m|h)$/);
|
|
3309
|
+
if (!match) {
|
|
3310
|
+
throw new BuildError("execution.timeout must use ms, s, m, or h units");
|
|
3311
|
+
}
|
|
3312
|
+
const amount = Number(match[1]);
|
|
3313
|
+
const unit = match[2];
|
|
3314
|
+
const multiplier = unit === "ms" ? 1 : unit === "s" ? 1e3 : unit === "m" ? 6e4 : 36e5;
|
|
3315
|
+
return amount * multiplier;
|
|
3316
|
+
}
|
|
3317
|
+
function parseMemoryMb(value) {
|
|
3318
|
+
if (value === void 0) return void 0;
|
|
3319
|
+
if (typeof value === "number") {
|
|
3320
|
+
if (Number.isInteger(value) && value > 0) return value;
|
|
3321
|
+
throw new BuildError("execution.memory must be a positive integer number of megabytes");
|
|
3322
|
+
}
|
|
3323
|
+
if (typeof value !== "string") {
|
|
3324
|
+
throw new BuildError("execution.memory must be a size string or megabytes");
|
|
3325
|
+
}
|
|
3326
|
+
const match = value.trim().toLowerCase().match(/^(\d+)\s*(mb|m|gb|g)$/);
|
|
3327
|
+
if (!match) {
|
|
3328
|
+
throw new BuildError("execution.memory must use mb or gb units");
|
|
3329
|
+
}
|
|
3330
|
+
const amount = Number(match[1]);
|
|
3331
|
+
const unit = match[2];
|
|
3332
|
+
return unit === "gb" || unit === "g" ? amount * 1024 : amount;
|
|
3333
|
+
}
|
|
3334
|
+
function nonNegativeInteger(value, field) {
|
|
3335
|
+
if (value === void 0) return void 0;
|
|
3336
|
+
if (Number.isInteger(value) && value >= 0) return value;
|
|
3337
|
+
throw new BuildError(`${field} must be a non-negative integer`);
|
|
3338
|
+
}
|
|
3339
|
+
function normalizeBudgetMap(value, field) {
|
|
3340
|
+
if (value === void 0) return void 0;
|
|
3341
|
+
if (!value || typeof value !== "object" || Array.isArray(value)) {
|
|
3342
|
+
throw new BuildError(`${field} must be an object`);
|
|
3343
|
+
}
|
|
3344
|
+
const out = {};
|
|
3345
|
+
for (const [key, budget] of Object.entries(value)) {
|
|
3346
|
+
if (!key.trim()) {
|
|
3347
|
+
throw new BuildError(`${field} keys must be non-empty strings`);
|
|
3348
|
+
}
|
|
3349
|
+
out[key] = nonNegativeInteger(budget, `${field}.${key}`);
|
|
3350
|
+
}
|
|
3351
|
+
return out;
|
|
3352
|
+
}
|
|
3353
|
+
function normalizeRateBudget(rateBudget) {
|
|
3354
|
+
if (rateBudget === void 0) return void 0;
|
|
3355
|
+
const perApplication = normalizeBudgetMap(
|
|
3356
|
+
rateBudget.perApplication,
|
|
3357
|
+
"execution.rateBudget.perApplication"
|
|
3358
|
+
);
|
|
3359
|
+
const perCapability = normalizeBudgetMap(
|
|
3360
|
+
rateBudget.perCapability,
|
|
3361
|
+
"execution.rateBudget.perCapability"
|
|
3362
|
+
);
|
|
3363
|
+
const out = {
|
|
3364
|
+
...perApplication ? { per_application: perApplication } : {},
|
|
3365
|
+
...perCapability ? { per_capability: perCapability } : {}
|
|
3366
|
+
};
|
|
3367
|
+
return Object.keys(out).length > 0 ? out : void 0;
|
|
3368
|
+
}
|
|
2570
3369
|
function compactExecution(execution) {
|
|
2571
3370
|
if (!execution) {
|
|
2572
3371
|
return void 0;
|
|
2573
3372
|
}
|
|
2574
|
-
const
|
|
2575
|
-
|
|
2576
|
-
);
|
|
3373
|
+
const timeoutMs = parseDurationMs(execution.timeout);
|
|
3374
|
+
const memoryMb = parseMemoryMb(execution.memory);
|
|
3375
|
+
const maxToolCalls = nonNegativeInteger(execution.maxToolCalls, "execution.maxToolCalls");
|
|
3376
|
+
const rateBudget = normalizeRateBudget(execution.rateBudget);
|
|
3377
|
+
const compact = {
|
|
3378
|
+
...timeoutMs !== void 0 ? { timeout_ms: timeoutMs } : {},
|
|
3379
|
+
...memoryMb !== void 0 ? { memory_mb: memoryMb } : {},
|
|
3380
|
+
...maxToolCalls !== void 0 ? { max_tool_calls: maxToolCalls } : {},
|
|
3381
|
+
...rateBudget !== void 0 ? { rate_budget: rateBudget } : {}
|
|
3382
|
+
};
|
|
2577
3383
|
return Object.keys(compact).length > 0 ? compact : void 0;
|
|
2578
3384
|
}
|
|
2579
3385
|
function buildBehaviorReplacement(source, spec, behavior) {
|
|
@@ -2583,8 +3389,6 @@ function buildBehaviorReplacement(source, spec, behavior) {
|
|
|
2583
3389
|
}
|
|
2584
3390
|
const execution = compactExecution(behavior.execution);
|
|
2585
3391
|
const runSource = functionSourceFromProperty(source, run, "run");
|
|
2586
|
-
const trackingId = findProperty(spec, "trackingId");
|
|
2587
|
-
const trackingIdSource = trackingId ? functionSourceFromProperty(source, trackingId, "trackingId") : void 0;
|
|
2588
3392
|
const applications = findProperty(spec, "applications");
|
|
2589
3393
|
const applicationsSource = applications ? expressionSourceFromProperty(source, applications, "applications") : void 0;
|
|
2590
3394
|
const configEntries = [
|
|
@@ -2595,9 +3399,6 @@ function buildBehaviorReplacement(source, spec, behavior) {
|
|
|
2595
3399
|
if (execution) {
|
|
2596
3400
|
configEntries.push(`execution: ${JSON.stringify(execution)}`);
|
|
2597
3401
|
}
|
|
2598
|
-
if (behavior.messageHistory !== void 0) {
|
|
2599
|
-
configEntries.push(`messageHistory: ${JSON.stringify(behavior.messageHistory)}`);
|
|
2600
|
-
}
|
|
2601
3402
|
if (applicationsSource) {
|
|
2602
3403
|
configEntries.push(`applications: ${INTERNAL_APPLICATIONS}`);
|
|
2603
3404
|
}
|
|
@@ -2606,17 +3407,14 @@ function buildBehaviorReplacement(source, spec, behavior) {
|
|
|
2606
3407
|
replacement: [
|
|
2607
3408
|
...applicationsSource ? [`const ${INTERNAL_APPLICATIONS} = ${applicationsSource};`] : [],
|
|
2608
3409
|
`const ${INTERNAL_CONFIG} = ${configSource};`,
|
|
2609
|
-
`const ${INTERNAL_TRACKING_ID} = ${trackingIdSource ?? "undefined"};`,
|
|
2610
3410
|
`const ${INTERNAL_RUN} = ${runSource};`,
|
|
2611
|
-
`export { ${INTERNAL_CONFIG} as config, ${
|
|
3411
|
+
`export { ${INTERNAL_CONFIG} as config, ${INTERNAL_RUN} as run };`
|
|
2612
3412
|
].join("\n"),
|
|
2613
3413
|
refs: /* @__PURE__ */ new Set([
|
|
2614
3414
|
INTERNAL_CONFIG,
|
|
2615
|
-
INTERNAL_TRACKING_ID,
|
|
2616
3415
|
INTERNAL_RUN,
|
|
2617
3416
|
...applicationsSource ? [INTERNAL_APPLICATIONS] : [],
|
|
2618
3417
|
...refsFromExpressionSource(runSource),
|
|
2619
|
-
...trackingIdSource ? refsFromExpressionSource(trackingIdSource) : [],
|
|
2620
3418
|
...applicationsSource ? refsFromExpressionSource(applicationsSource) : []
|
|
2621
3419
|
])
|
|
2622
3420
|
};
|
|
@@ -2756,7 +3554,15 @@ async function buildPipeline(config, cwd = process.cwd(), minify = false, buildD
|
|
|
2756
3554
|
conversationBundles
|
|
2757
3555
|
});
|
|
2758
3556
|
validateManifest(manifest);
|
|
2759
|
-
|
|
3557
|
+
const { catalog: catalog2, warnings } = generateCatalog(manifest);
|
|
3558
|
+
if (warnings.length > 0) {
|
|
3559
|
+
throw new BuildError(
|
|
3560
|
+
`Catalog generation produced ${warnings.length} unrecoverable issue${warnings.length === 1 ? "" : "s"} \u2014 refusing to ship a partial catalog:
|
|
3561
|
+
${warnings.map((w) => ` - ${w}`).join("\n")}`
|
|
3562
|
+
);
|
|
3563
|
+
}
|
|
3564
|
+
await writeFile(join(outDir, "catalog.json"), stableStringify(catalog2, 2), "utf-8");
|
|
3565
|
+
return { manifest, catalog: catalog2, outDir };
|
|
2760
3566
|
}
|
|
2761
3567
|
async function createStagingDir() {
|
|
2762
3568
|
return mkdtemp(join(tmpdir(), "io-cli-"));
|
|
@@ -2776,7 +3582,7 @@ async function buildCommand(options) {
|
|
|
2776
3582
|
const config = await loadConfig({
|
|
2777
3583
|
dir: options.dir
|
|
2778
3584
|
});
|
|
2779
|
-
console.log(
|
|
3585
|
+
console.log(chalk12.gray("Validating deployment..."));
|
|
2780
3586
|
stagingDir = await createStagingDir();
|
|
2781
3587
|
const { manifest } = await buildPipeline(
|
|
2782
3588
|
config,
|
|
@@ -2806,10 +3612,10 @@ async function buildCommand(options) {
|
|
|
2806
3612
|
`${subscriptions.length} subscription${subscriptions.length === 1 ? "" : "s"}`,
|
|
2807
3613
|
`${secretBundles.length} secret bundle${secretBundles.length === 1 ? "" : "s"}`
|
|
2808
3614
|
];
|
|
2809
|
-
console.log(
|
|
3615
|
+
console.log(chalk12.green(`\u2713 Build succeeded (${parts.join(", ")})`));
|
|
2810
3616
|
} catch (err) {
|
|
2811
3617
|
if (err instanceof CliError) {
|
|
2812
|
-
console.error(
|
|
3618
|
+
console.error(chalk12.red(err.message));
|
|
2813
3619
|
process.exit(1);
|
|
2814
3620
|
}
|
|
2815
3621
|
throw err;
|
|
@@ -2820,8 +3626,8 @@ async function buildCommand(options) {
|
|
|
2820
3626
|
async function zipBuildOutput(buildDir, outPath) {
|
|
2821
3627
|
const output = createWriteStream(outPath);
|
|
2822
3628
|
const archive = archiver("zip", { zlib: { level: 9 } });
|
|
2823
|
-
const done = new Promise((
|
|
2824
|
-
output.on("close",
|
|
3629
|
+
const done = new Promise((resolve5, reject) => {
|
|
3630
|
+
output.on("close", resolve5);
|
|
2825
3631
|
archive.on("error", reject);
|
|
2826
3632
|
});
|
|
2827
3633
|
archive.pipe(output);
|
|
@@ -2850,13 +3656,13 @@ async function addDirectory(archive, baseDir, prefix, skipRelativePath) {
|
|
|
2850
3656
|
}
|
|
2851
3657
|
|
|
2852
3658
|
// src/deploy/diff.ts
|
|
2853
|
-
function
|
|
3659
|
+
function stableStringify2(value) {
|
|
2854
3660
|
if (Array.isArray(value)) {
|
|
2855
|
-
return `[${value.map(
|
|
3661
|
+
return `[${value.map(stableStringify2).join(",")}]`;
|
|
2856
3662
|
}
|
|
2857
3663
|
if (value && typeof value === "object") {
|
|
2858
3664
|
const record = value;
|
|
2859
|
-
return `{${Object.keys(record).sort().map((key) => `${JSON.stringify(key)}:${
|
|
3665
|
+
return `{${Object.keys(record).sort().map((key) => `${JSON.stringify(key)}:${stableStringify2(record[key])}`).join(",")}}`;
|
|
2860
3666
|
}
|
|
2861
3667
|
return JSON.stringify(value);
|
|
2862
3668
|
}
|
|
@@ -2882,7 +3688,7 @@ function diffEntries(local, remote) {
|
|
|
2882
3688
|
for (const { name, value } of local) {
|
|
2883
3689
|
if (!remoteMap.has(name)) {
|
|
2884
3690
|
entries.push({ name, kind: "added" });
|
|
2885
|
-
} else if (
|
|
3691
|
+
} else if (stableStringify2(value) !== stableStringify2(remoteMap.get(name))) {
|
|
2886
3692
|
entries.push({ name, kind: "changed" });
|
|
2887
3693
|
}
|
|
2888
3694
|
}
|
|
@@ -2995,10 +3801,81 @@ function diffManifests(local, remote) {
|
|
|
2995
3801
|
hasChanges
|
|
2996
3802
|
};
|
|
2997
3803
|
}
|
|
3804
|
+
|
|
3805
|
+
// src/deploy/catalog-diff.ts
|
|
3806
|
+
function stableStringify3(value) {
|
|
3807
|
+
if (Array.isArray(value)) {
|
|
3808
|
+
return `[${value.map(stableStringify3).join(",")}]`;
|
|
3809
|
+
}
|
|
3810
|
+
if (value && typeof value === "object") {
|
|
3811
|
+
const record = value;
|
|
3812
|
+
return `{${Object.keys(record).sort().map((key) => `${JSON.stringify(key)}:${stableStringify3(record[key])}`).join(",")}}`;
|
|
3813
|
+
}
|
|
3814
|
+
return JSON.stringify(value);
|
|
3815
|
+
}
|
|
3816
|
+
function diffSection(local, remote) {
|
|
3817
|
+
const localMap = local ?? {};
|
|
3818
|
+
const remoteMap = remote ?? {};
|
|
3819
|
+
const entries = [];
|
|
3820
|
+
const seen = /* @__PURE__ */ new Set();
|
|
3821
|
+
for (const id of Object.keys(localMap).sort()) {
|
|
3822
|
+
seen.add(id);
|
|
3823
|
+
if (!(id in remoteMap)) {
|
|
3824
|
+
entries.push({ id, kind: "added" });
|
|
3825
|
+
} else if (stableStringify3(localMap[id]) !== stableStringify3(remoteMap[id])) {
|
|
3826
|
+
entries.push({ id, kind: "changed" });
|
|
3827
|
+
}
|
|
3828
|
+
}
|
|
3829
|
+
for (const id of Object.keys(remoteMap).sort()) {
|
|
3830
|
+
if (!seen.has(id) && !(id in localMap)) {
|
|
3831
|
+
entries.push({ id, kind: "removed" });
|
|
3832
|
+
}
|
|
3833
|
+
}
|
|
3834
|
+
return entries;
|
|
3835
|
+
}
|
|
3836
|
+
function schemaDiffCounts(local, remote) {
|
|
3837
|
+
const localKeys = new Set(Object.keys(local ?? {}));
|
|
3838
|
+
const remoteKeys = new Set(Object.keys(remote ?? {}));
|
|
3839
|
+
let added = 0;
|
|
3840
|
+
let removed = 0;
|
|
3841
|
+
for (const k of localKeys) {
|
|
3842
|
+
if (!remoteKeys.has(k)) added++;
|
|
3843
|
+
}
|
|
3844
|
+
for (const k of remoteKeys) {
|
|
3845
|
+
if (!localKeys.has(k)) removed++;
|
|
3846
|
+
}
|
|
3847
|
+
return { added, removed };
|
|
3848
|
+
}
|
|
3849
|
+
function diffCatalogs(local, remote) {
|
|
3850
|
+
const localResources = local?.resources ?? {};
|
|
3851
|
+
const remoteResources = remote?.resources ?? {};
|
|
3852
|
+
const localSignals = local?.signals ?? {};
|
|
3853
|
+
const remoteSignals = remote?.signals ?? {};
|
|
3854
|
+
const localTools = local?.tools ?? {};
|
|
3855
|
+
const remoteTools = remote?.tools ?? {};
|
|
3856
|
+
const localSchemas = local?.schemas ?? {};
|
|
3857
|
+
const remoteSchemas = remote?.schemas ?? {};
|
|
3858
|
+
const resources = diffSection(localResources, remoteResources);
|
|
3859
|
+
const signals2 = diffSection(localSignals, remoteSignals);
|
|
3860
|
+
const tools = diffSection(localTools, remoteTools);
|
|
3861
|
+
const schemas = schemaDiffCounts(localSchemas, remoteSchemas);
|
|
3862
|
+
const localHash = local?.catalog_hash ?? null;
|
|
3863
|
+
const remoteHash = remote?.catalog_hash ?? null;
|
|
3864
|
+
const hashChanged = (localHash ?? remoteHash) !== null && localHash !== remoteHash;
|
|
3865
|
+
const hasChanges = resources.length > 0 || signals2.length > 0 || tools.length > 0 || schemas.added > 0 || schemas.removed > 0 || hashChanged;
|
|
3866
|
+
return {
|
|
3867
|
+
hashChanged,
|
|
3868
|
+
resources,
|
|
3869
|
+
signals: signals2,
|
|
3870
|
+
tools,
|
|
3871
|
+
schemas,
|
|
3872
|
+
hasChanges
|
|
3873
|
+
};
|
|
3874
|
+
}
|
|
2998
3875
|
var SYMBOLS = {
|
|
2999
|
-
added:
|
|
3000
|
-
changed:
|
|
3001
|
-
removed:
|
|
3876
|
+
added: chalk12.green("+ "),
|
|
3877
|
+
changed: chalk12.yellow("~ "),
|
|
3878
|
+
removed: chalk12.red("- ")
|
|
3002
3879
|
};
|
|
3003
3880
|
var LABELS = {
|
|
3004
3881
|
added: "new",
|
|
@@ -3014,30 +3891,64 @@ var SECTIONS = [
|
|
|
3014
3891
|
{ key: "targets", label: "Targets" },
|
|
3015
3892
|
{ key: "subscriptions", label: "Subscriptions" }
|
|
3016
3893
|
];
|
|
3017
|
-
|
|
3894
|
+
var CATALOG_SECTIONS = [
|
|
3895
|
+
{ key: "resources", label: "Catalog resources" },
|
|
3896
|
+
{ key: "signals", label: "Catalog signals" },
|
|
3897
|
+
{ key: "tools", label: "Catalog tools" }
|
|
3898
|
+
];
|
|
3899
|
+
function formatPlan(diff, catalogDiff) {
|
|
3018
3900
|
const lines = [];
|
|
3019
3901
|
for (const { key, label } of SECTIONS) {
|
|
3020
3902
|
const entries = diff[key];
|
|
3021
3903
|
lines.push(` ${label}:`);
|
|
3022
3904
|
if (entries.length === 0) {
|
|
3023
|
-
lines.push(
|
|
3905
|
+
lines.push(chalk12.gray(" (no changes)"));
|
|
3024
3906
|
} else {
|
|
3025
3907
|
for (const e of entries) {
|
|
3026
3908
|
lines.push(
|
|
3027
|
-
` ${SYMBOLS[e.kind]}${e.name} ${
|
|
3909
|
+
` ${SYMBOLS[e.kind]}${e.name} ${chalk12.gray(`(${LABELS[e.kind]})`)}`
|
|
3028
3910
|
);
|
|
3029
3911
|
}
|
|
3030
3912
|
}
|
|
3031
3913
|
}
|
|
3914
|
+
if (catalogDiff) {
|
|
3915
|
+
lines.push("");
|
|
3916
|
+
lines.push(
|
|
3917
|
+
chalk12.bold(
|
|
3918
|
+
catalogDiff.hashChanged ? " Catalog (hash changed):" : " Catalog:"
|
|
3919
|
+
)
|
|
3920
|
+
);
|
|
3921
|
+
for (const { key, label } of CATALOG_SECTIONS) {
|
|
3922
|
+
const entries = catalogDiff[key];
|
|
3923
|
+
lines.push(` ${label}:`);
|
|
3924
|
+
if (entries.length === 0) {
|
|
3925
|
+
lines.push(chalk12.gray(" (no changes)"));
|
|
3926
|
+
} else {
|
|
3927
|
+
for (const e of entries) {
|
|
3928
|
+
lines.push(
|
|
3929
|
+
` ${SYMBOLS[e.kind]}${e.id} ${chalk12.gray(`(${LABELS[e.kind]})`)}`
|
|
3930
|
+
);
|
|
3931
|
+
}
|
|
3932
|
+
}
|
|
3933
|
+
}
|
|
3934
|
+
const schemaLine = catalogDiff.schemas;
|
|
3935
|
+
if (schemaLine.added > 0 || schemaLine.removed > 0) {
|
|
3936
|
+
lines.push(
|
|
3937
|
+
` ${chalk12.gray("Schemas:")} ${chalk12.green(`+${schemaLine.added}`)} ${chalk12.red(`-${schemaLine.removed}`)}`
|
|
3938
|
+
);
|
|
3939
|
+
} else {
|
|
3940
|
+
lines.push(` ${chalk12.gray("Schemas: (no changes)")}`);
|
|
3941
|
+
}
|
|
3942
|
+
}
|
|
3032
3943
|
return lines.join("\n");
|
|
3033
3944
|
}
|
|
3034
3945
|
async function confirmPlan(skip) {
|
|
3035
3946
|
if (skip) return true;
|
|
3036
3947
|
const rl = createInterface({ input: process.stdin, output: process.stdout });
|
|
3037
|
-
return new Promise((
|
|
3948
|
+
return new Promise((resolve5) => {
|
|
3038
3949
|
rl.question("\nApply these changes? (y/n) ", (answer) => {
|
|
3039
3950
|
rl.close();
|
|
3040
|
-
|
|
3951
|
+
resolve5(answer.trim().toLowerCase() === "y");
|
|
3041
3952
|
});
|
|
3042
3953
|
});
|
|
3043
3954
|
}
|
|
@@ -3111,40 +4022,6 @@ function socketAuthParams(creds) {
|
|
|
3111
4022
|
return { authorization: `Bearer ${creds.token}` };
|
|
3112
4023
|
}
|
|
3113
4024
|
|
|
3114
|
-
// src/build/stable-json.ts
|
|
3115
|
-
function stableStringify2(value, indent) {
|
|
3116
|
-
return JSON.stringify(canonicalize(value), null, indent);
|
|
3117
|
-
}
|
|
3118
|
-
function canonicalize(value) {
|
|
3119
|
-
if (value === null) return null;
|
|
3120
|
-
if (Array.isArray(value)) {
|
|
3121
|
-
return value.map(canonicalize);
|
|
3122
|
-
}
|
|
3123
|
-
if (typeof value === "object") {
|
|
3124
|
-
const obj = value;
|
|
3125
|
-
const sorted = {};
|
|
3126
|
-
for (const key of Object.keys(obj).sort()) {
|
|
3127
|
-
sorted[key] = canonicalize(obj[key]);
|
|
3128
|
-
}
|
|
3129
|
-
return sorted;
|
|
3130
|
-
}
|
|
3131
|
-
return value;
|
|
3132
|
-
}
|
|
3133
|
-
|
|
3134
|
-
// src/runtime-signal-name.ts
|
|
3135
|
-
var EVENT_NAME_REGEX2 = /^[A-Za-z0-9][A-Za-z0-9._-]*$/;
|
|
3136
|
-
var SIGNAL_NAME_REGEX = /^signal:[A-Za-z0-9][A-Za-z0-9._-]*$/;
|
|
3137
|
-
function normalizeRuntimeSignalName2(value) {
|
|
3138
|
-
const trimmed = value.trim();
|
|
3139
|
-
if (EVENT_NAME_REGEX2.test(trimmed)) {
|
|
3140
|
-
return `signal:${trimmed}`;
|
|
3141
|
-
}
|
|
3142
|
-
if (SIGNAL_NAME_REGEX.test(trimmed)) {
|
|
3143
|
-
return trimmed;
|
|
3144
|
-
}
|
|
3145
|
-
return null;
|
|
3146
|
-
}
|
|
3147
|
-
|
|
3148
4025
|
// src/api/client.ts
|
|
3149
4026
|
var REFRESH_MUTATION = `
|
|
3150
4027
|
mutation Refresh($input: RefreshAuthInput!) {
|
|
@@ -3245,8 +4122,32 @@ function graphqlErrorMessage(errors) {
|
|
|
3245
4122
|
}
|
|
3246
4123
|
return null;
|
|
3247
4124
|
}
|
|
3248
|
-
function
|
|
3249
|
-
return
|
|
4125
|
+
function isPlainObject(value) {
|
|
4126
|
+
return !!value && typeof value === "object" && !Array.isArray(value);
|
|
4127
|
+
}
|
|
4128
|
+
function isCatalogShape(value) {
|
|
4129
|
+
if (!isPlainObject(value)) return false;
|
|
4130
|
+
return value.contract_version === "io.invocation.v2" && value.snapshot_version === "io_runtime.snapshot.v2" && typeof value.catalog_hash === "string" && isPlainObject(value.resources) && isPlainObject(value.signals) && isPlainObject(value.tools) && isPlainObject(value.schemas);
|
|
4131
|
+
}
|
|
4132
|
+
function isManifestShape(value) {
|
|
4133
|
+
if (!isPlainObject(value)) return false;
|
|
4134
|
+
return value.contract_version === "io.invocation.v2" && value.snapshot_version === "io_runtime.snapshot.v2" && Array.isArray(value.secret_bundles) && Array.isArray(value.apps) && Array.isArray(value.endpoints) && Array.isArray(value.targets) && Array.isArray(value.subscriptions);
|
|
4135
|
+
}
|
|
4136
|
+
function parseRemoteCatalog(catalogJson) {
|
|
4137
|
+
let parsed;
|
|
4138
|
+
try {
|
|
4139
|
+
parsed = JSON.parse(catalogJson);
|
|
4140
|
+
} catch (err) {
|
|
4141
|
+
throw new ConfigError(
|
|
4142
|
+
`Remote deployment catalog was not valid JSON: ${err instanceof Error ? err.message : String(err)}`
|
|
4143
|
+
);
|
|
4144
|
+
}
|
|
4145
|
+
if (!isCatalogShape(parsed)) {
|
|
4146
|
+
throw new ConfigError(
|
|
4147
|
+
"Remote deployment catalog does not match the io.invocation.v2 catalog shape."
|
|
4148
|
+
);
|
|
4149
|
+
}
|
|
4150
|
+
return parsed;
|
|
3250
4151
|
}
|
|
3251
4152
|
function parseRemoteManifest(snapshotJson) {
|
|
3252
4153
|
let parsed;
|
|
@@ -3257,11 +4158,22 @@ function parseRemoteManifest(snapshotJson) {
|
|
|
3257
4158
|
`Remote deployment snapshot was not valid JSON: ${err instanceof Error ? err.message : String(err)}`
|
|
3258
4159
|
);
|
|
3259
4160
|
}
|
|
3260
|
-
if (!
|
|
3261
|
-
throw new ConfigError(
|
|
4161
|
+
if (!isManifestShape(parsed)) {
|
|
4162
|
+
throw new ConfigError(
|
|
4163
|
+
"Remote deployment snapshot does not match the io_runtime.snapshot.v2 manifest shape."
|
|
4164
|
+
);
|
|
3262
4165
|
}
|
|
3263
4166
|
return parsed;
|
|
3264
4167
|
}
|
|
4168
|
+
var EMPTY_MANIFEST = {
|
|
4169
|
+
contract_version: "io.invocation.v2",
|
|
4170
|
+
snapshot_version: "io_runtime.snapshot.v2",
|
|
4171
|
+
secret_bundles: [],
|
|
4172
|
+
apps: [],
|
|
4173
|
+
endpoints: [],
|
|
4174
|
+
targets: [],
|
|
4175
|
+
subscriptions: []
|
|
4176
|
+
};
|
|
3265
4177
|
async function executeGraphQLUnauth(apiUrl, query, variables, headers = {}) {
|
|
3266
4178
|
const response = await fetch(`${trimBaseUrl(apiUrl)}/graphql`, {
|
|
3267
4179
|
method: "POST",
|
|
@@ -3478,14 +4390,33 @@ function createClient(config) {
|
|
|
3478
4390
|
);
|
|
3479
4391
|
return data.createProject;
|
|
3480
4392
|
}
|
|
3481
|
-
async function
|
|
4393
|
+
async function deleteProject(organizationId, projectIdValue) {
|
|
3482
4394
|
const data = await graphql(
|
|
3483
4395
|
`
|
|
3484
|
-
|
|
3485
|
-
|
|
4396
|
+
mutation DeleteProject($organizationId: ID!, $id: ID!) {
|
|
4397
|
+
deleteProject(organizationId: $organizationId, id: $id) {
|
|
4398
|
+
id
|
|
3486
4399
|
name
|
|
4400
|
+
slug
|
|
4401
|
+
description
|
|
4402
|
+
organizationId
|
|
4403
|
+
}
|
|
4404
|
+
}
|
|
4405
|
+
`,
|
|
4406
|
+
{ organizationId, id: projectIdValue }
|
|
4407
|
+
);
|
|
4408
|
+
return data.deleteProject;
|
|
4409
|
+
}
|
|
4410
|
+
async function principals2(projectIdValue) {
|
|
4411
|
+
const data = await graphql(
|
|
4412
|
+
`
|
|
4413
|
+
query Principals($projectId: ID!) {
|
|
4414
|
+
principals(projectId: $projectId) {
|
|
4415
|
+
id
|
|
4416
|
+
externalId
|
|
4417
|
+
state
|
|
4418
|
+
metadata
|
|
3487
4419
|
projectId
|
|
3488
|
-
hasValue
|
|
3489
4420
|
insertedAt
|
|
3490
4421
|
updatedAt
|
|
3491
4422
|
}
|
|
@@ -3493,9 +4424,83 @@ function createClient(config) {
|
|
|
3493
4424
|
`,
|
|
3494
4425
|
{ projectId: projectIdValue }
|
|
3495
4426
|
);
|
|
3496
|
-
return data.
|
|
4427
|
+
return data.principals ?? [];
|
|
3497
4428
|
}
|
|
3498
|
-
async function
|
|
4429
|
+
async function createPrincipal(projectIdValue, input) {
|
|
4430
|
+
const data = await graphql(
|
|
4431
|
+
`
|
|
4432
|
+
mutation CreatePrincipal($projectId: ID!, $input: CreatePrincipalInput!) {
|
|
4433
|
+
createPrincipal(projectId: $projectId, input: $input) {
|
|
4434
|
+
id
|
|
4435
|
+
externalId
|
|
4436
|
+
state
|
|
4437
|
+
metadata
|
|
4438
|
+
projectId
|
|
4439
|
+
insertedAt
|
|
4440
|
+
updatedAt
|
|
4441
|
+
}
|
|
4442
|
+
}
|
|
4443
|
+
`,
|
|
4444
|
+
{ projectId: projectIdValue, input }
|
|
4445
|
+
);
|
|
4446
|
+
return data.createPrincipal;
|
|
4447
|
+
}
|
|
4448
|
+
async function updatePrincipal(projectIdValue, id, input) {
|
|
4449
|
+
const data = await graphql(
|
|
4450
|
+
`
|
|
4451
|
+
mutation UpdatePrincipal($projectId: ID!, $id: ID!, $input: UpdatePrincipalInput!) {
|
|
4452
|
+
updatePrincipal(projectId: $projectId, id: $id, input: $input) {
|
|
4453
|
+
id
|
|
4454
|
+
externalId
|
|
4455
|
+
state
|
|
4456
|
+
metadata
|
|
4457
|
+
projectId
|
|
4458
|
+
insertedAt
|
|
4459
|
+
updatedAt
|
|
4460
|
+
}
|
|
4461
|
+
}
|
|
4462
|
+
`,
|
|
4463
|
+
{ projectId: projectIdValue, id, input }
|
|
4464
|
+
);
|
|
4465
|
+
return data.updatePrincipal ?? null;
|
|
4466
|
+
}
|
|
4467
|
+
async function deletePrincipal(projectIdValue, id) {
|
|
4468
|
+
const data = await graphql(
|
|
4469
|
+
`
|
|
4470
|
+
mutation DeletePrincipal($projectId: ID!, $id: ID!) {
|
|
4471
|
+
deletePrincipal(projectId: $projectId, id: $id) {
|
|
4472
|
+
id
|
|
4473
|
+
externalId
|
|
4474
|
+
state
|
|
4475
|
+
metadata
|
|
4476
|
+
projectId
|
|
4477
|
+
insertedAt
|
|
4478
|
+
updatedAt
|
|
4479
|
+
}
|
|
4480
|
+
}
|
|
4481
|
+
`,
|
|
4482
|
+
{ projectId: projectIdValue, id }
|
|
4483
|
+
);
|
|
4484
|
+
return data.deletePrincipal ?? null;
|
|
4485
|
+
}
|
|
4486
|
+
async function listSecrets(projectIdValue) {
|
|
4487
|
+
const data = await graphql(
|
|
4488
|
+
`
|
|
4489
|
+
query Secrets($projectId: ID!) {
|
|
4490
|
+
secrets(projectId: $projectId) {
|
|
4491
|
+
name
|
|
4492
|
+
projectId
|
|
4493
|
+
hasValue
|
|
4494
|
+
insertedAt
|
|
4495
|
+
updatedAt
|
|
4496
|
+
}
|
|
4497
|
+
}
|
|
4498
|
+
`,
|
|
4499
|
+
{ projectId: projectIdValue }
|
|
4500
|
+
);
|
|
4501
|
+
return data.secrets ?? [];
|
|
4502
|
+
}
|
|
4503
|
+
async function putSecret(projectIdValue, input) {
|
|
3499
4504
|
const data = await graphql(
|
|
3500
4505
|
`
|
|
3501
4506
|
mutation PutSecret($input: PutSecretInput!) {
|
|
@@ -3531,28 +4536,34 @@ function createClient(config) {
|
|
|
3531
4536
|
return data.deleteSecret === true;
|
|
3532
4537
|
}
|
|
3533
4538
|
async function fetchRemoteManifest(projectIdValue) {
|
|
3534
|
-
|
|
3535
|
-
|
|
3536
|
-
data = await graphql(
|
|
3537
|
-
`
|
|
4539
|
+
const data = await graphql(
|
|
4540
|
+
`
|
|
3538
4541
|
query ActiveDeploymentSnapshot($projectId: ID!) {
|
|
3539
4542
|
activeDeployment(projectId: $projectId) {
|
|
3540
4543
|
snapshotJson
|
|
3541
4544
|
}
|
|
3542
4545
|
}
|
|
3543
4546
|
`,
|
|
3544
|
-
|
|
3545
|
-
|
|
3546
|
-
} catch (err) {
|
|
3547
|
-
if (isMissingSnapshotJsonFieldError(err)) {
|
|
3548
|
-
return {};
|
|
3549
|
-
}
|
|
3550
|
-
throw err;
|
|
3551
|
-
}
|
|
4547
|
+
{ projectId: projectIdValue }
|
|
4548
|
+
);
|
|
3552
4549
|
const snapshotJson = data.activeDeployment?.snapshotJson;
|
|
3553
|
-
return typeof snapshotJson === "string" && snapshotJson.trim() !== "" ? parseRemoteManifest(snapshotJson) :
|
|
4550
|
+
return typeof snapshotJson === "string" && snapshotJson.trim() !== "" ? parseRemoteManifest(snapshotJson) : EMPTY_MANIFEST;
|
|
3554
4551
|
}
|
|
3555
|
-
async function
|
|
4552
|
+
async function fetchRemoteCatalog(projectIdValue) {
|
|
4553
|
+
const data = await graphql(
|
|
4554
|
+
`
|
|
4555
|
+
query ActiveDeploymentCatalog($projectId: ID!) {
|
|
4556
|
+
activeDeployment(projectId: $projectId) {
|
|
4557
|
+
catalogJson
|
|
4558
|
+
}
|
|
4559
|
+
}
|
|
4560
|
+
`,
|
|
4561
|
+
{ projectId: projectIdValue }
|
|
4562
|
+
);
|
|
4563
|
+
const catalogJson = data.activeDeployment?.catalogJson;
|
|
4564
|
+
return typeof catalogJson === "string" && catalogJson.trim() !== "" ? parseRemoteCatalog(catalogJson) : null;
|
|
4565
|
+
}
|
|
4566
|
+
async function deployBundle(bundlePath, manifest, catalog2) {
|
|
3556
4567
|
const MAX_BUNDLE_BYTES = 100 * 1024 * 1024;
|
|
3557
4568
|
const fileStat = await stat(bundlePath);
|
|
3558
4569
|
if (fileStat.size > MAX_BUNDLE_BYTES) {
|
|
@@ -3590,7 +4601,8 @@ function createClient(config) {
|
|
|
3590
4601
|
projectId: projectIdValue,
|
|
3591
4602
|
bundleHash: upload.bundleHash,
|
|
3592
4603
|
revision: nextRevision,
|
|
3593
|
-
snapshotJson:
|
|
4604
|
+
snapshotJson: stableStringify(manifest),
|
|
4605
|
+
catalogJson: stableStringify(catalog2)
|
|
3594
4606
|
}
|
|
3595
4607
|
}
|
|
3596
4608
|
);
|
|
@@ -3598,7 +4610,10 @@ function createClient(config) {
|
|
|
3598
4610
|
}
|
|
3599
4611
|
async function emitSignal(input) {
|
|
3600
4612
|
const projectIdValue = input.projectId ?? projectId();
|
|
3601
|
-
|
|
4613
|
+
if (!input.principalId || input.principalId.trim() === "") {
|
|
4614
|
+
throw new ConfigError("principalId is required to emit a signal");
|
|
4615
|
+
}
|
|
4616
|
+
const eventName = normalizeRuntimeSignalName(input.signal);
|
|
3602
4617
|
if (!eventName) {
|
|
3603
4618
|
throw new ConfigError(
|
|
3604
4619
|
"signal name must be a bare event name like smoke.start or use the signal: namespace"
|
|
@@ -3625,6 +4640,7 @@ function createClient(config) {
|
|
|
3625
4640
|
{
|
|
3626
4641
|
input: {
|
|
3627
4642
|
projectId: projectIdValue,
|
|
4643
|
+
principalId: input.principalId,
|
|
3628
4644
|
eventName,
|
|
3629
4645
|
payloadJson: JSON.stringify(input.payload ?? {}),
|
|
3630
4646
|
metadataJson: JSON.stringify(input.metadata ?? {}),
|
|
@@ -3650,6 +4666,37 @@ function createClient(config) {
|
|
|
3650
4666
|
);
|
|
3651
4667
|
return data.activeRevision;
|
|
3652
4668
|
}
|
|
4669
|
+
async function reconcileProject(projectIdValue) {
|
|
4670
|
+
const data = await graphql(
|
|
4671
|
+
`
|
|
4672
|
+
mutation ReconcileProject($projectId: ID!) {
|
|
4673
|
+
reconcileProject(projectId: $projectId)
|
|
4674
|
+
}
|
|
4675
|
+
`,
|
|
4676
|
+
{ projectId: projectIdValue }
|
|
4677
|
+
);
|
|
4678
|
+
return data.reconcileProject === true;
|
|
4679
|
+
}
|
|
4680
|
+
async function deploymentStatus(projectIdValue, revision) {
|
|
4681
|
+
const data = await graphql(
|
|
4682
|
+
`
|
|
4683
|
+
query DeploymentStatus($projectId: ID!, $revision: Int!) {
|
|
4684
|
+
deploymentStatus(projectId: $projectId, revision: $revision) {
|
|
4685
|
+
id
|
|
4686
|
+
projectId
|
|
4687
|
+
revision
|
|
4688
|
+
bundleHash
|
|
4689
|
+
status
|
|
4690
|
+
activatedAt
|
|
4691
|
+
insertedAt
|
|
4692
|
+
updatedAt
|
|
4693
|
+
}
|
|
4694
|
+
}
|
|
4695
|
+
`,
|
|
4696
|
+
{ projectId: projectIdValue, revision }
|
|
4697
|
+
);
|
|
4698
|
+
return data.deploymentStatus;
|
|
4699
|
+
}
|
|
3653
4700
|
async function quotaStatuses() {
|
|
3654
4701
|
const data = await graphql(
|
|
3655
4702
|
`
|
|
@@ -3701,27 +4748,263 @@ function createClient(config) {
|
|
|
3701
4748
|
organizations,
|
|
3702
4749
|
projects: projects2,
|
|
3703
4750
|
createProject,
|
|
4751
|
+
deleteProject,
|
|
4752
|
+
principals: principals2,
|
|
4753
|
+
createPrincipal,
|
|
4754
|
+
updatePrincipal,
|
|
4755
|
+
deletePrincipal,
|
|
3704
4756
|
listSecrets,
|
|
3705
4757
|
putSecret,
|
|
3706
4758
|
deleteSecret,
|
|
3707
4759
|
fetchRemoteManifest,
|
|
4760
|
+
fetchRemoteCatalog,
|
|
3708
4761
|
deployBundle,
|
|
4762
|
+
deploymentStatus,
|
|
4763
|
+
reconcileProject,
|
|
3709
4764
|
emitSignal,
|
|
3710
4765
|
quotaStatuses,
|
|
3711
4766
|
projectEvents
|
|
3712
4767
|
};
|
|
3713
4768
|
}
|
|
4769
|
+
Object.assign(globalThis, { WebSocket });
|
|
4770
|
+
var SessionTokenNotSupportedError = class extends Error {
|
|
4771
|
+
constructor(message) {
|
|
4772
|
+
super(
|
|
4773
|
+
message ?? "WebSocket subscriptions require API key authentication on the current io_runtime socket contract."
|
|
4774
|
+
);
|
|
4775
|
+
this.name = "SessionTokenNotSupportedError";
|
|
4776
|
+
}
|
|
4777
|
+
};
|
|
4778
|
+
function buildSocketUrl(apiUrl) {
|
|
4779
|
+
const url = new URL(apiUrl);
|
|
4780
|
+
const basePath = url.pathname.replace(/\/+$/, "");
|
|
4781
|
+
const socketBase = basePath.endsWith("/graphql") ? basePath.slice(0, -"/graphql".length) : basePath;
|
|
4782
|
+
url.protocol = url.protocol === "https:" ? "wss:" : "ws:";
|
|
4783
|
+
url.pathname = `${socketBase === "/" ? "" : socketBase}/socket`;
|
|
4784
|
+
url.search = "";
|
|
4785
|
+
url.hash = "";
|
|
4786
|
+
return url.toString();
|
|
4787
|
+
}
|
|
4788
|
+
function createSocketClient(options) {
|
|
4789
|
+
let phoenixSocket = null;
|
|
4790
|
+
let absintheSocket = null;
|
|
4791
|
+
const notifiers = /* @__PURE__ */ new Set();
|
|
4792
|
+
function ensureConnection() {
|
|
4793
|
+
if (!options.credentials.apiKey) {
|
|
4794
|
+
throw new SessionTokenNotSupportedError();
|
|
4795
|
+
}
|
|
4796
|
+
if (!phoenixSocket || !absintheSocket) {
|
|
4797
|
+
phoenixSocket = new Socket(buildSocketUrl(options.apiUrl), {
|
|
4798
|
+
params: socketAuthParams(options.credentials),
|
|
4799
|
+
timeout: options.timeoutMs ?? 1e4
|
|
4800
|
+
});
|
|
4801
|
+
absintheSocket = AbsintheSocket.create(phoenixSocket);
|
|
4802
|
+
phoenixSocket.connect();
|
|
4803
|
+
}
|
|
4804
|
+
return { absintheSocket };
|
|
4805
|
+
}
|
|
4806
|
+
function tearDownIfIdle() {
|
|
4807
|
+
if (notifiers.size === 0 && phoenixSocket) {
|
|
4808
|
+
phoenixSocket.disconnect();
|
|
4809
|
+
phoenixSocket = null;
|
|
4810
|
+
absintheSocket = null;
|
|
4811
|
+
}
|
|
4812
|
+
}
|
|
4813
|
+
return {
|
|
4814
|
+
subscribe(operation, variables, observer) {
|
|
4815
|
+
const conn = ensureConnection();
|
|
4816
|
+
const notifier = AbsintheSocket.send(conn.absintheSocket, {
|
|
4817
|
+
operation,
|
|
4818
|
+
variables
|
|
4819
|
+
});
|
|
4820
|
+
notifiers.add(notifier);
|
|
4821
|
+
AbsintheSocket.observe(conn.absintheSocket, notifier, {
|
|
4822
|
+
onAbort: (err) => {
|
|
4823
|
+
notifiers.delete(notifier);
|
|
4824
|
+
if (observer.onAbort) {
|
|
4825
|
+
observer.onAbort(err);
|
|
4826
|
+
} else {
|
|
4827
|
+
observer.onError?.(err);
|
|
4828
|
+
}
|
|
4829
|
+
tearDownIfIdle();
|
|
4830
|
+
},
|
|
4831
|
+
onCancel: () => {
|
|
4832
|
+
notifiers.delete(notifier);
|
|
4833
|
+
tearDownIfIdle();
|
|
4834
|
+
},
|
|
4835
|
+
onError: (err) => {
|
|
4836
|
+
observer.onError?.(err);
|
|
4837
|
+
},
|
|
4838
|
+
onResult: (result) => {
|
|
4839
|
+
observer.onResult?.(result);
|
|
4840
|
+
},
|
|
4841
|
+
onStart: () => {
|
|
4842
|
+
observer.onStart?.();
|
|
4843
|
+
}
|
|
4844
|
+
});
|
|
4845
|
+
return () => {
|
|
4846
|
+
if (!notifiers.has(notifier)) return;
|
|
4847
|
+
notifiers.delete(notifier);
|
|
4848
|
+
try {
|
|
4849
|
+
AbsintheSocket.cancel(conn.absintheSocket, notifier);
|
|
4850
|
+
} catch {
|
|
4851
|
+
}
|
|
4852
|
+
tearDownIfIdle();
|
|
4853
|
+
};
|
|
4854
|
+
},
|
|
4855
|
+
close() {
|
|
4856
|
+
notifiers.clear();
|
|
4857
|
+
if (phoenixSocket) {
|
|
4858
|
+
phoenixSocket.disconnect();
|
|
4859
|
+
}
|
|
4860
|
+
phoenixSocket = null;
|
|
4861
|
+
absintheSocket = null;
|
|
4862
|
+
}
|
|
4863
|
+
};
|
|
4864
|
+
}
|
|
4865
|
+
|
|
4866
|
+
// src/api/watcher.ts
|
|
4867
|
+
var DEFAULT_POLL_INTERVAL_MS = 2e3;
|
|
4868
|
+
var DEFAULT_SEEN_ID_LIMIT = 1e3;
|
|
4869
|
+
var DEFAULT_POLL_SAFETY_LOOPS = 10;
|
|
4870
|
+
function installSigintHandler(onTerminate) {
|
|
4871
|
+
const handler = () => {
|
|
4872
|
+
onTerminate();
|
|
4873
|
+
};
|
|
4874
|
+
process.once("SIGINT", handler);
|
|
4875
|
+
return () => {
|
|
4876
|
+
process.removeListener("SIGINT", handler);
|
|
4877
|
+
};
|
|
4878
|
+
}
|
|
4879
|
+
async function watch(plan, handlers) {
|
|
4880
|
+
const seenIds = /* @__PURE__ */ new Set();
|
|
4881
|
+
const seenOrder = [];
|
|
4882
|
+
const seenLimit = plan.seenIdLimit ?? DEFAULT_SEEN_ID_LIMIT;
|
|
4883
|
+
function rememberId(id) {
|
|
4884
|
+
if (seenIds.has(id)) return;
|
|
4885
|
+
seenIds.add(id);
|
|
4886
|
+
seenOrder.push(id);
|
|
4887
|
+
while (seenOrder.length > seenLimit) {
|
|
4888
|
+
const evicted = seenOrder.shift();
|
|
4889
|
+
if (evicted) seenIds.delete(evicted);
|
|
4890
|
+
}
|
|
4891
|
+
}
|
|
4892
|
+
function handle(event) {
|
|
4893
|
+
const id = plan.getId(event);
|
|
4894
|
+
if (seenIds.has(id)) return false;
|
|
4895
|
+
rememberId(id);
|
|
4896
|
+
handlers.onEvent(event);
|
|
4897
|
+
return true;
|
|
4898
|
+
}
|
|
4899
|
+
let cursor = plan.initialSince ?? (/* @__PURE__ */ new Date()).toISOString();
|
|
4900
|
+
function advanceCursor(event) {
|
|
4901
|
+
if (!plan.getCursor) return;
|
|
4902
|
+
const next = plan.getCursor(event);
|
|
4903
|
+
if (next > cursor) cursor = next;
|
|
4904
|
+
}
|
|
4905
|
+
if (plan.snapshot) {
|
|
4906
|
+
const initial = await plan.snapshot();
|
|
4907
|
+
for (const event of initial) {
|
|
4908
|
+
handle(event);
|
|
4909
|
+
advanceCursor(event);
|
|
4910
|
+
if (plan.isTerminal?.(event)) return;
|
|
4911
|
+
}
|
|
4912
|
+
}
|
|
4913
|
+
return new Promise((resolve5, reject) => {
|
|
4914
|
+
let cleanupSigint;
|
|
4915
|
+
let subscribeHandle;
|
|
4916
|
+
let pollTimer;
|
|
4917
|
+
let terminated = false;
|
|
4918
|
+
function finalize(action) {
|
|
4919
|
+
if (terminated) return;
|
|
4920
|
+
terminated = true;
|
|
4921
|
+
subscribeHandle?.unsubscribe();
|
|
4922
|
+
if (pollTimer) clearTimeout(pollTimer);
|
|
4923
|
+
cleanupSigint?.();
|
|
4924
|
+
action();
|
|
4925
|
+
}
|
|
4926
|
+
cleanupSigint = installSigintHandler(() => {
|
|
4927
|
+
finalize(() => resolve5());
|
|
4928
|
+
});
|
|
4929
|
+
function processLiveEvent(event) {
|
|
4930
|
+
const accepted = handle(event);
|
|
4931
|
+
if (accepted) advanceCursor(event);
|
|
4932
|
+
if (plan.isTerminal?.(event)) finalize(() => resolve5());
|
|
4933
|
+
}
|
|
4934
|
+
async function startPolling() {
|
|
4935
|
+
if (!plan.poll) {
|
|
4936
|
+
finalize(() => reject(new Error("No polling fallback configured for this watch.")));
|
|
4937
|
+
return;
|
|
4938
|
+
}
|
|
4939
|
+
handlers.onConnected?.("polling");
|
|
4940
|
+
const interval = plan.pollIntervalMs ?? DEFAULT_POLL_INTERVAL_MS;
|
|
4941
|
+
const safetyLoops = plan.pollBatchSafetyLoops ?? DEFAULT_POLL_SAFETY_LOOPS;
|
|
4942
|
+
const loop = async () => {
|
|
4943
|
+
if (terminated) return;
|
|
4944
|
+
try {
|
|
4945
|
+
for (let i = 0; i < safetyLoops; i += 1) {
|
|
4946
|
+
const batch = await plan.poll(cursor);
|
|
4947
|
+
let anyNew = false;
|
|
4948
|
+
for (const event of batch) {
|
|
4949
|
+
if (handle(event)) anyNew = true;
|
|
4950
|
+
advanceCursor(event);
|
|
4951
|
+
if (plan.isTerminal?.(event)) {
|
|
4952
|
+
finalize(() => resolve5());
|
|
4953
|
+
return;
|
|
4954
|
+
}
|
|
4955
|
+
}
|
|
4956
|
+
if (!anyNew) break;
|
|
4957
|
+
}
|
|
4958
|
+
} catch (err) {
|
|
4959
|
+
finalize(
|
|
4960
|
+
() => reject(err instanceof Error ? err : new Error(String(err)))
|
|
4961
|
+
);
|
|
4962
|
+
return;
|
|
4963
|
+
}
|
|
4964
|
+
pollTimer = setTimeout(loop, interval);
|
|
4965
|
+
};
|
|
4966
|
+
void loop();
|
|
4967
|
+
}
|
|
4968
|
+
if (!plan.subscribe) {
|
|
4969
|
+
void startPolling();
|
|
4970
|
+
return;
|
|
4971
|
+
}
|
|
4972
|
+
try {
|
|
4973
|
+
subscribeHandle = plan.subscribe(
|
|
4974
|
+
processLiveEvent,
|
|
4975
|
+
(err) => {
|
|
4976
|
+
if (err instanceof SessionTokenNotSupportedError && plan.poll) {
|
|
4977
|
+
subscribeHandle = void 0;
|
|
4978
|
+
void startPolling();
|
|
4979
|
+
return;
|
|
4980
|
+
}
|
|
4981
|
+
finalize(() => reject(err));
|
|
4982
|
+
},
|
|
4983
|
+
() => handlers.onConnected?.("subscription")
|
|
4984
|
+
);
|
|
4985
|
+
} catch (err) {
|
|
4986
|
+
if (err instanceof SessionTokenNotSupportedError && plan.poll) {
|
|
4987
|
+
void startPolling();
|
|
4988
|
+
return;
|
|
4989
|
+
}
|
|
4990
|
+
finalize(() => reject(err instanceof Error ? err : new Error(String(err))));
|
|
4991
|
+
}
|
|
4992
|
+
});
|
|
4993
|
+
}
|
|
3714
4994
|
|
|
3715
4995
|
// src/commands/deploy.ts
|
|
4996
|
+
var TERMINAL_STATUSES = /* @__PURE__ */ new Set(["active", "failed", "superseded"]);
|
|
4997
|
+
var WATCH_POLL_INTERVAL_MS = 2e3;
|
|
4998
|
+
var WATCH_DEADLINE_MS = 5 * 6e4;
|
|
3716
4999
|
async function deployCommand(options) {
|
|
3717
5000
|
let stagingDir;
|
|
3718
5001
|
try {
|
|
3719
5002
|
const config = await loadConfig({
|
|
3720
5003
|
dir: options.dir
|
|
3721
5004
|
});
|
|
3722
|
-
console.log(
|
|
5005
|
+
console.log(chalk12.gray("Planning deployment..."));
|
|
3723
5006
|
stagingDir = await createStagingDir();
|
|
3724
|
-
const { manifest } = await buildPipeline(
|
|
5007
|
+
const { manifest, catalog: catalog2 } = await buildPipeline(
|
|
3725
5008
|
config,
|
|
3726
5009
|
process.cwd(),
|
|
3727
5010
|
options.minify ?? true,
|
|
@@ -3729,7 +5012,7 @@ async function deployCommand(options) {
|
|
|
3729
5012
|
);
|
|
3730
5013
|
if (!config.apiUrl || !config.projectId) {
|
|
3731
5014
|
console.log(
|
|
3732
|
-
|
|
5015
|
+
chalk12.yellow(
|
|
3733
5016
|
"\nNo apiUrl or projectId configured. Skipping upload."
|
|
3734
5017
|
)
|
|
3735
5018
|
);
|
|
@@ -3739,30 +5022,37 @@ async function deployCommand(options) {
|
|
|
3739
5022
|
apiUrl: config.apiUrl,
|
|
3740
5023
|
projectId: config.projectId
|
|
3741
5024
|
});
|
|
3742
|
-
console.log(
|
|
3743
|
-
const remoteManifest = await client.fetchRemoteManifest(
|
|
5025
|
+
console.log(chalk12.gray("\nComparing with remote..."));
|
|
5026
|
+
const remoteManifest = await client.fetchRemoteManifest(
|
|
5027
|
+
client.projectId()
|
|
5028
|
+
);
|
|
5029
|
+
const remoteCatalog = await client.fetchRemoteCatalog(client.projectId());
|
|
3744
5030
|
const diff = diffManifests(manifest, remoteManifest);
|
|
3745
|
-
|
|
3746
|
-
|
|
5031
|
+
const catalogDiff = diffCatalogs(catalog2, remoteCatalog);
|
|
5032
|
+
if (!diff.hasChanges && !catalogDiff.hasChanges) {
|
|
5033
|
+
console.log(chalk12.green("\n\u2713 No changes to deploy."));
|
|
3747
5034
|
return;
|
|
3748
5035
|
}
|
|
3749
|
-
console.log("\n" + formatPlan(diff));
|
|
5036
|
+
console.log("\n" + formatPlan(diff, catalogDiff));
|
|
3750
5037
|
const confirmed = await confirmPlan(options.yes ?? false);
|
|
3751
5038
|
if (!confirmed) {
|
|
3752
|
-
console.log(
|
|
5039
|
+
console.log(chalk12.yellow("Deploy cancelled."));
|
|
3753
5040
|
return;
|
|
3754
5041
|
}
|
|
3755
|
-
console.log(
|
|
5042
|
+
console.log(chalk12.gray("\nApplying deployment..."));
|
|
3756
5043
|
const zipPath = join(stagingDir, "deploy.zip");
|
|
3757
5044
|
await zipBuildOutput(stagingDir, zipPath);
|
|
3758
|
-
const revision = await client.deployBundle(zipPath, manifest);
|
|
5045
|
+
const revision = await client.deployBundle(zipPath, manifest, catalog2);
|
|
3759
5046
|
console.log(
|
|
3760
|
-
|
|
3761
|
-
\u2713 Deployment
|
|
5047
|
+
chalk12.green(`
|
|
5048
|
+
\u2713 Deployment submitted (revision ${revision.revision}, ${revision.status})`)
|
|
3762
5049
|
);
|
|
5050
|
+
if (options.watch) {
|
|
5051
|
+
await watchDeployment(client, revision);
|
|
5052
|
+
}
|
|
3763
5053
|
} catch (err) {
|
|
3764
5054
|
if (err instanceof CliError) {
|
|
3765
|
-
console.error(
|
|
5055
|
+
console.error(chalk12.red(err.message));
|
|
3766
5056
|
process.exit(1);
|
|
3767
5057
|
}
|
|
3768
5058
|
throw err;
|
|
@@ -3770,6 +5060,72 @@ async function deployCommand(options) {
|
|
|
3770
5060
|
await cleanupStagingDir(stagingDir);
|
|
3771
5061
|
}
|
|
3772
5062
|
}
|
|
5063
|
+
function colorStatus(status) {
|
|
5064
|
+
switch (status) {
|
|
5065
|
+
case "active":
|
|
5066
|
+
return chalk12.green(status);
|
|
5067
|
+
case "failed":
|
|
5068
|
+
return chalk12.red(status);
|
|
5069
|
+
case "superseded":
|
|
5070
|
+
return chalk12.yellow(status);
|
|
5071
|
+
case "staged":
|
|
5072
|
+
return chalk12.cyan(status);
|
|
5073
|
+
case "draft":
|
|
5074
|
+
default:
|
|
5075
|
+
return chalk12.gray(status);
|
|
5076
|
+
}
|
|
5077
|
+
}
|
|
5078
|
+
async function watchDeployment(client, initial) {
|
|
5079
|
+
if (TERMINAL_STATUSES.has(initial.status)) {
|
|
5080
|
+
return;
|
|
5081
|
+
}
|
|
5082
|
+
console.log(chalk12.gray(`
|
|
5083
|
+
Watching revision ${initial.revision}\u2026`));
|
|
5084
|
+
let lastStatus = initial.status;
|
|
5085
|
+
const startedAt = Date.now();
|
|
5086
|
+
await watch(
|
|
5087
|
+
{
|
|
5088
|
+
poll: async () => {
|
|
5089
|
+
if (Date.now() - startedAt > WATCH_DEADLINE_MS) {
|
|
5090
|
+
throw new Error(
|
|
5091
|
+
`Deployment did not reach a terminal status within ${WATCH_DEADLINE_MS / 1e3}s.`
|
|
5092
|
+
);
|
|
5093
|
+
}
|
|
5094
|
+
const next = await client.deploymentStatus(
|
|
5095
|
+
client.projectId(),
|
|
5096
|
+
initial.revision
|
|
5097
|
+
);
|
|
5098
|
+
return next ? [next] : [];
|
|
5099
|
+
},
|
|
5100
|
+
pollIntervalMs: WATCH_POLL_INTERVAL_MS,
|
|
5101
|
+
getId: (rev) => `${rev.revision}:${rev.status}:${rev.updatedAt}`,
|
|
5102
|
+
isTerminal: (rev) => TERMINAL_STATUSES.has(rev.status)
|
|
5103
|
+
},
|
|
5104
|
+
{
|
|
5105
|
+
onEvent: (rev) => {
|
|
5106
|
+
if (rev.status !== lastStatus) {
|
|
5107
|
+
console.log(
|
|
5108
|
+
` revision ${rev.revision} \u2192 ${colorStatus(rev.status)}`
|
|
5109
|
+
);
|
|
5110
|
+
lastStatus = rev.status;
|
|
5111
|
+
}
|
|
5112
|
+
}
|
|
5113
|
+
}
|
|
5114
|
+
);
|
|
5115
|
+
if (lastStatus === "active") {
|
|
5116
|
+
console.log(chalk12.green(`
|
|
5117
|
+
\u2713 Deployment active (revision ${initial.revision}).`));
|
|
5118
|
+
} else if (lastStatus === "failed") {
|
|
5119
|
+
console.log(chalk12.red(`
|
|
5120
|
+
\u2717 Deployment failed (revision ${initial.revision}).`));
|
|
5121
|
+
process.exit(1);
|
|
5122
|
+
} else if (lastStatus === "superseded") {
|
|
5123
|
+
console.log(
|
|
5124
|
+
chalk12.yellow(`
|
|
5125
|
+
! Revision ${initial.revision} was superseded by a later deploy.`)
|
|
5126
|
+
);
|
|
5127
|
+
}
|
|
5128
|
+
}
|
|
3773
5129
|
var SUCCESS_HTML = `<!DOCTYPE html>
|
|
3774
5130
|
<html>
|
|
3775
5131
|
<head><title>IO CLI</title><style>
|
|
@@ -3873,7 +5229,7 @@ async function loginCommand(options) {
|
|
|
3873
5229
|
try {
|
|
3874
5230
|
if (options.token) {
|
|
3875
5231
|
await saveToken(options.token, true);
|
|
3876
|
-
console.log(
|
|
5232
|
+
console.log(chalk12.green("\u2713 API key saved"));
|
|
3877
5233
|
return;
|
|
3878
5234
|
}
|
|
3879
5235
|
const config = await loadConfig();
|
|
@@ -3884,7 +5240,7 @@ async function loginCommand(options) {
|
|
|
3884
5240
|
}
|
|
3885
5241
|
const existing = await loadToken();
|
|
3886
5242
|
if (existing) {
|
|
3887
|
-
console.log(
|
|
5243
|
+
console.log(chalk12.gray("Existing session found. Re-authenticating..."));
|
|
3888
5244
|
}
|
|
3889
5245
|
const callbackServer = await startCallbackServer();
|
|
3890
5246
|
const pkce = generatePkcePair();
|
|
@@ -3894,12 +5250,12 @@ async function loginCommand(options) {
|
|
|
3894
5250
|
callbackServer.state,
|
|
3895
5251
|
pkce.challenge
|
|
3896
5252
|
);
|
|
3897
|
-
console.log(
|
|
5253
|
+
console.log(chalk12.gray("Opening your browser for sign-in..."));
|
|
3898
5254
|
try {
|
|
3899
5255
|
await open(authorizationUrl);
|
|
3900
5256
|
} catch {
|
|
3901
|
-
console.log(
|
|
3902
|
-
console.log(
|
|
5257
|
+
console.log(chalk12.yellow("Could not open the browser automatically."));
|
|
5258
|
+
console.log(chalk12.gray(`Open this URL manually:
|
|
3903
5259
|
${authorizationUrl}`));
|
|
3904
5260
|
}
|
|
3905
5261
|
try {
|
|
@@ -3923,10 +5279,10 @@ ${authorizationUrl}`));
|
|
|
3923
5279
|
} finally {
|
|
3924
5280
|
callbackServer.close();
|
|
3925
5281
|
}
|
|
3926
|
-
console.log(
|
|
5282
|
+
console.log(chalk12.green("\n\u2713 Authenticated successfully"));
|
|
3927
5283
|
} catch (err) {
|
|
3928
5284
|
if (err instanceof CliError) {
|
|
3929
|
-
console.error(
|
|
5285
|
+
console.error(chalk12.red(err.message));
|
|
3930
5286
|
process.exit(1);
|
|
3931
5287
|
}
|
|
3932
5288
|
throw err;
|
|
@@ -3936,10 +5292,10 @@ async function logoutCommand() {
|
|
|
3936
5292
|
const creds = await loadCredentials();
|
|
3937
5293
|
await clearToken();
|
|
3938
5294
|
if (!creds) {
|
|
3939
|
-
console.log(
|
|
5295
|
+
console.log(chalk12.yellow("Not logged in."));
|
|
3940
5296
|
return;
|
|
3941
5297
|
}
|
|
3942
|
-
console.log(
|
|
5298
|
+
console.log(chalk12.green("\u2713 Logged out"));
|
|
3943
5299
|
}
|
|
3944
5300
|
async function hydrateIdentity() {
|
|
3945
5301
|
const creds = await loadCredentials();
|
|
@@ -3983,35 +5339,35 @@ async function hydrateIdentity() {
|
|
|
3983
5339
|
async function whoamiCommand() {
|
|
3984
5340
|
const { creds, sessionName, sessionEmail } = await hydrateIdentity();
|
|
3985
5341
|
if (!creds) {
|
|
3986
|
-
console.log(
|
|
5342
|
+
console.log(chalk12.yellow("Not logged in. Run `io login` to authenticate."));
|
|
3987
5343
|
return;
|
|
3988
5344
|
}
|
|
3989
|
-
console.log(
|
|
5345
|
+
console.log(chalk12.green("\u2713 Logged in"));
|
|
3990
5346
|
const displayUser = sessionName ?? sessionEmail ?? creds.userId;
|
|
3991
5347
|
if (displayUser) {
|
|
3992
|
-
console.log(
|
|
5348
|
+
console.log(chalk12.gray(` User: ${displayUser}`));
|
|
3993
5349
|
}
|
|
3994
5350
|
if (sessionEmail && sessionEmail !== displayUser) {
|
|
3995
|
-
console.log(
|
|
5351
|
+
console.log(chalk12.gray(` Email: ${sessionEmail}`));
|
|
3996
5352
|
}
|
|
3997
5353
|
if (creds.organizationId) {
|
|
3998
|
-
console.log(
|
|
5354
|
+
console.log(chalk12.gray(` Organization: ${creds.organizationId}`));
|
|
3999
5355
|
}
|
|
4000
5356
|
if (creds.apiKey) {
|
|
4001
|
-
console.log(
|
|
5357
|
+
console.log(chalk12.gray(" Session: API key"));
|
|
4002
5358
|
} else if (creds.refreshToken) {
|
|
4003
|
-
console.log(
|
|
5359
|
+
console.log(chalk12.gray(" Session: refresh token stored"));
|
|
4004
5360
|
} else {
|
|
4005
|
-
console.log(
|
|
5361
|
+
console.log(chalk12.gray(" Session: token only (no refresh)"));
|
|
4006
5362
|
}
|
|
4007
5363
|
}
|
|
4008
5364
|
var CONFIG_FILE = "io.config.json";
|
|
4009
5365
|
async function prompt(question) {
|
|
4010
5366
|
const rl = createInterface({ input: process.stdin, output: process.stdout });
|
|
4011
|
-
return new Promise((
|
|
5367
|
+
return new Promise((resolve5) => {
|
|
4012
5368
|
rl.question(question, (answer) => {
|
|
4013
5369
|
rl.close();
|
|
4014
|
-
|
|
5370
|
+
resolve5(answer.trim());
|
|
4015
5371
|
});
|
|
4016
5372
|
});
|
|
4017
5373
|
}
|
|
@@ -4032,13 +5388,13 @@ async function selectOrganization(organizations, preferredId) {
|
|
|
4032
5388
|
if (organizations.length === 1) {
|
|
4033
5389
|
return organizations[0];
|
|
4034
5390
|
}
|
|
4035
|
-
console.log(
|
|
5391
|
+
console.log(chalk12.gray("\nAvailable organizations:"));
|
|
4036
5392
|
organizations.forEach((organization, index2) => {
|
|
4037
5393
|
console.log(
|
|
4038
|
-
` ${
|
|
5394
|
+
` ${chalk12.green(String(index2 + 1))}. ${organization.name} ${chalk12.gray(organization.id)}`
|
|
4039
5395
|
);
|
|
4040
5396
|
});
|
|
4041
|
-
const choice = await prompt(
|
|
5397
|
+
const choice = await prompt(chalk12.gray("\nSelect an organization (number or ID): "));
|
|
4042
5398
|
const index = Number(choice);
|
|
4043
5399
|
if (index > 0 && index <= organizations.length) {
|
|
4044
5400
|
return organizations[index - 1];
|
|
@@ -4051,16 +5407,16 @@ async function initCommand(options) {
|
|
|
4051
5407
|
const existing = await readExistingConfig(cwd);
|
|
4052
5408
|
if (existing.projectId && !options.yes) {
|
|
4053
5409
|
const overwrite = await prompt(
|
|
4054
|
-
|
|
5410
|
+
chalk12.yellow(`io.config.json already has projectId "${existing.projectId}". Overwrite? (y/n) `)
|
|
4055
5411
|
);
|
|
4056
5412
|
if (overwrite.toLowerCase() !== "y") {
|
|
4057
|
-
console.log(
|
|
5413
|
+
console.log(chalk12.gray("Cancelled."));
|
|
4058
5414
|
return;
|
|
4059
5415
|
}
|
|
4060
5416
|
}
|
|
4061
5417
|
const config = { ...existing };
|
|
4062
5418
|
if (!config.apiUrl) {
|
|
4063
|
-
const apiUrl = await prompt(
|
|
5419
|
+
const apiUrl = await prompt(chalk12.gray("API URL (leave blank for default): "));
|
|
4064
5420
|
if (apiUrl) {
|
|
4065
5421
|
config.apiUrl = apiUrl;
|
|
4066
5422
|
}
|
|
@@ -4082,7 +5438,7 @@ async function initCommand(options) {
|
|
|
4082
5438
|
try {
|
|
4083
5439
|
organizations = await client.organizations();
|
|
4084
5440
|
} catch {
|
|
4085
|
-
console.log(
|
|
5441
|
+
console.log(chalk12.yellow("Could not fetch organizations. Enter project ID manually."));
|
|
4086
5442
|
}
|
|
4087
5443
|
const selectedOrganization = await selectOrganization(
|
|
4088
5444
|
organizations,
|
|
@@ -4091,14 +5447,14 @@ async function initCommand(options) {
|
|
|
4091
5447
|
if (selectedOrganization) {
|
|
4092
5448
|
const projects2 = await client.projects(selectedOrganization.id);
|
|
4093
5449
|
if (projects2.length > 0) {
|
|
4094
|
-
console.log(
|
|
5450
|
+
console.log(chalk12.gray("\nAvailable projects:"));
|
|
4095
5451
|
projects2.forEach((project, index2) => {
|
|
4096
5452
|
console.log(
|
|
4097
|
-
` ${
|
|
5453
|
+
` ${chalk12.green(String(index2 + 1))}. ${project.name} ${chalk12.gray(project.id)}`
|
|
4098
5454
|
);
|
|
4099
5455
|
});
|
|
4100
5456
|
const choice = await prompt(
|
|
4101
|
-
|
|
5457
|
+
chalk12.gray("\nSelect a project (number) or enter a project ID: ")
|
|
4102
5458
|
);
|
|
4103
5459
|
const index = Number(choice);
|
|
4104
5460
|
if (index > 0 && index <= projects2.length) {
|
|
@@ -4108,26 +5464,26 @@ async function initCommand(options) {
|
|
|
4108
5464
|
}
|
|
4109
5465
|
} else {
|
|
4110
5466
|
const createNew = await prompt(
|
|
4111
|
-
|
|
5467
|
+
chalk12.gray("No projects found. Create one? (y/n) ")
|
|
4112
5468
|
);
|
|
4113
5469
|
if (createNew.toLowerCase() === "y") {
|
|
4114
|
-
const name = await prompt(
|
|
5470
|
+
const name = await prompt(chalk12.gray("Project name: "));
|
|
4115
5471
|
if (name) {
|
|
4116
5472
|
const project = await client.createProject(selectedOrganization.id, name);
|
|
4117
5473
|
config.projectId = project.id;
|
|
4118
|
-
console.log(
|
|
5474
|
+
console.log(chalk12.green(`\u2713 Created project: ${project.name}`));
|
|
4119
5475
|
}
|
|
4120
5476
|
}
|
|
4121
5477
|
}
|
|
4122
5478
|
} else {
|
|
4123
|
-
const projectId = await prompt(
|
|
5479
|
+
const projectId = await prompt(chalk12.gray("Project ID (or leave blank): "));
|
|
4124
5480
|
if (projectId) {
|
|
4125
5481
|
config.projectId = projectId;
|
|
4126
5482
|
}
|
|
4127
5483
|
}
|
|
4128
5484
|
} else {
|
|
4129
|
-
console.log(
|
|
4130
|
-
const projectId = await prompt(
|
|
5485
|
+
console.log(chalk12.yellow("Not logged in. Run `io login` first to select a project."));
|
|
5486
|
+
const projectId = await prompt(chalk12.gray("Project ID (or leave blank): "));
|
|
4131
5487
|
if (projectId) {
|
|
4132
5488
|
config.projectId = projectId;
|
|
4133
5489
|
}
|
|
@@ -4137,14 +5493,14 @@ async function initCommand(options) {
|
|
|
4137
5493
|
JSON.stringify(config, null, 2) + "\n",
|
|
4138
5494
|
"utf-8"
|
|
4139
5495
|
);
|
|
4140
|
-
console.log(
|
|
5496
|
+
console.log(chalk12.green(`
|
|
4141
5497
|
\u2713 Wrote ${CONFIG_FILE}`));
|
|
4142
5498
|
if (config.projectId) {
|
|
4143
|
-
console.log(
|
|
5499
|
+
console.log(chalk12.gray(` Project: ${config.projectId}`));
|
|
4144
5500
|
}
|
|
4145
5501
|
} catch (err) {
|
|
4146
5502
|
if (err instanceof CliError) {
|
|
4147
|
-
console.error(
|
|
5503
|
+
console.error(chalk12.red(err.message));
|
|
4148
5504
|
process.exit(1);
|
|
4149
5505
|
}
|
|
4150
5506
|
throw err;
|
|
@@ -4172,16 +5528,65 @@ async function projectsListCommand() {
|
|
|
4172
5528
|
const organizationId = await resolveOrganizationId(client);
|
|
4173
5529
|
const projects2 = await client.projects(organizationId);
|
|
4174
5530
|
if (!projects2 || projects2.length === 0) {
|
|
4175
|
-
console.log(
|
|
5531
|
+
console.log(chalk12.gray("No projects found. Create one with `io projects create <name>`."));
|
|
4176
5532
|
return;
|
|
4177
5533
|
}
|
|
4178
5534
|
for (const p of projects2) {
|
|
4179
|
-
const current = p.id === config.projectId ?
|
|
4180
|
-
console.log(` ${p.name} ${
|
|
5535
|
+
const current = p.id === config.projectId ? chalk12.green(" (current)") : "";
|
|
5536
|
+
console.log(` ${p.name} ${chalk12.gray(p.id)}${current}`);
|
|
5537
|
+
}
|
|
5538
|
+
} catch (err) {
|
|
5539
|
+
if (err instanceof CliError) {
|
|
5540
|
+
console.error(chalk12.red(err.message));
|
|
5541
|
+
process.exit(1);
|
|
5542
|
+
}
|
|
5543
|
+
throw err;
|
|
5544
|
+
}
|
|
5545
|
+
}
|
|
5546
|
+
async function confirmDelete(projectName, projectId) {
|
|
5547
|
+
const rl = createInterface({ input: process.stdin, output: process.stdout });
|
|
5548
|
+
return new Promise((resolve5) => {
|
|
5549
|
+
rl.question(
|
|
5550
|
+
`Delete project "${projectName}" (${projectId})? Type the project name to confirm: `,
|
|
5551
|
+
(answer) => {
|
|
5552
|
+
rl.close();
|
|
5553
|
+
resolve5(answer.trim() === projectName);
|
|
5554
|
+
}
|
|
5555
|
+
);
|
|
5556
|
+
});
|
|
5557
|
+
}
|
|
5558
|
+
async function projectsDeleteCommand(id, options = {}) {
|
|
5559
|
+
try {
|
|
5560
|
+
const config = await loadConfig();
|
|
5561
|
+
const client = createClient(config);
|
|
5562
|
+
const organizationId = options.organizationId ?? await resolveOrganizationId(client);
|
|
5563
|
+
const projects2 = await client.projects(organizationId);
|
|
5564
|
+
const target = projects2.find((p) => p.id === id);
|
|
5565
|
+
if (!target) {
|
|
5566
|
+
throw new ConfigError(
|
|
5567
|
+
`Project "${id}" not found under organization ${organizationId}.`
|
|
5568
|
+
);
|
|
5569
|
+
}
|
|
5570
|
+
if (!options.yes) {
|
|
5571
|
+
const confirmed = await confirmDelete(target.name, target.id);
|
|
5572
|
+
if (!confirmed) {
|
|
5573
|
+
console.log(chalk12.yellow("Delete cancelled (name did not match)."));
|
|
5574
|
+
return;
|
|
5575
|
+
}
|
|
5576
|
+
}
|
|
5577
|
+
const deleted = await client.deleteProject(organizationId, target.id);
|
|
5578
|
+
console.log(chalk12.green(`\u2713 Deleted project: ${deleted.name}`));
|
|
5579
|
+
console.log(chalk12.gray(` ID: ${deleted.id}`));
|
|
5580
|
+
if (config.projectId === deleted.id) {
|
|
5581
|
+
console.log(
|
|
5582
|
+
chalk12.yellow(
|
|
5583
|
+
"\n io.config.json still references this project \u2014 clear the projectId field before redeploying."
|
|
5584
|
+
)
|
|
5585
|
+
);
|
|
4181
5586
|
}
|
|
4182
5587
|
} catch (err) {
|
|
4183
5588
|
if (err instanceof CliError) {
|
|
4184
|
-
console.error(
|
|
5589
|
+
console.error(chalk12.red(err.message));
|
|
4185
5590
|
process.exit(1);
|
|
4186
5591
|
}
|
|
4187
5592
|
throw err;
|
|
@@ -4193,10 +5598,10 @@ async function projectsCreateCommand(name) {
|
|
|
4193
5598
|
const client = createClient(config);
|
|
4194
5599
|
const organizationId = await resolveOrganizationId(client);
|
|
4195
5600
|
const project = await client.createProject(organizationId, name);
|
|
4196
|
-
console.log(
|
|
4197
|
-
console.log(
|
|
5601
|
+
console.log(chalk12.green(`\u2713 Created project: ${project.name}`));
|
|
5602
|
+
console.log(chalk12.gray(` ID: ${project.id}`));
|
|
4198
5603
|
console.log(
|
|
4199
|
-
|
|
5604
|
+
chalk12.gray(
|
|
4200
5605
|
`
|
|
4201
5606
|
Add to io.config.json:
|
|
4202
5607
|
{ "projectId": "${project.id}" }`
|
|
@@ -4204,7 +5609,7 @@ async function projectsCreateCommand(name) {
|
|
|
4204
5609
|
);
|
|
4205
5610
|
} catch (err) {
|
|
4206
5611
|
if (err instanceof CliError) {
|
|
4207
|
-
console.error(
|
|
5612
|
+
console.error(chalk12.red(err.message));
|
|
4208
5613
|
process.exit(1);
|
|
4209
5614
|
}
|
|
4210
5615
|
throw err;
|
|
@@ -4216,10 +5621,10 @@ async function secretsSetCommand(name, value) {
|
|
|
4216
5621
|
const client = createClient(config);
|
|
4217
5622
|
const pid = client.projectId();
|
|
4218
5623
|
await client.putSecret(pid, { name, value });
|
|
4219
|
-
console.log(
|
|
5624
|
+
console.log(chalk12.green(`\u2713 Set secret: ${name}`));
|
|
4220
5625
|
} catch (err) {
|
|
4221
5626
|
if (err instanceof CliError) {
|
|
4222
|
-
console.error(
|
|
5627
|
+
console.error(chalk12.red(err.message));
|
|
4223
5628
|
process.exit(1);
|
|
4224
5629
|
}
|
|
4225
5630
|
throw err;
|
|
@@ -4231,16 +5636,16 @@ async function secretsListCommand() {
|
|
|
4231
5636
|
const client = createClient(config);
|
|
4232
5637
|
const secrets2 = await client.listSecrets(client.projectId());
|
|
4233
5638
|
if (!secrets2 || secrets2.length === 0) {
|
|
4234
|
-
console.log(
|
|
5639
|
+
console.log(chalk12.gray("No secrets configured."));
|
|
4235
5640
|
return;
|
|
4236
5641
|
}
|
|
4237
5642
|
for (const s of secrets2) {
|
|
4238
|
-
const value = s.hasValue ? "" :
|
|
5643
|
+
const value = s.hasValue ? "" : chalk12.gray(" (no value)");
|
|
4239
5644
|
console.log(` ${s.name}${value}`);
|
|
4240
5645
|
}
|
|
4241
5646
|
} catch (err) {
|
|
4242
5647
|
if (err instanceof CliError) {
|
|
4243
|
-
console.error(
|
|
5648
|
+
console.error(chalk12.red(err.message));
|
|
4244
5649
|
process.exit(1);
|
|
4245
5650
|
}
|
|
4246
5651
|
throw err;
|
|
@@ -4252,25 +5657,123 @@ async function secretsRemoveCommand(name) {
|
|
|
4252
5657
|
const client = createClient(config);
|
|
4253
5658
|
const removed = await client.deleteSecret(client.projectId(), name);
|
|
4254
5659
|
if (!removed) {
|
|
4255
|
-
console.log(
|
|
5660
|
+
console.log(chalk12.yellow(`Secret "${name}" not found.`));
|
|
4256
5661
|
return;
|
|
4257
5662
|
}
|
|
4258
|
-
console.log(
|
|
5663
|
+
console.log(chalk12.green(`\u2713 Removed secret: ${name}`));
|
|
4259
5664
|
} catch (err) {
|
|
4260
5665
|
if (err instanceof CliError) {
|
|
4261
|
-
console.error(
|
|
5666
|
+
console.error(chalk12.red(err.message));
|
|
4262
5667
|
process.exit(1);
|
|
4263
5668
|
}
|
|
4264
5669
|
throw err;
|
|
4265
5670
|
}
|
|
4266
5671
|
}
|
|
5672
|
+
function parseMetadata(input) {
|
|
5673
|
+
if (!input) return void 0;
|
|
5674
|
+
try {
|
|
5675
|
+
const parsed = JSON.parse(input);
|
|
5676
|
+
if (parsed && typeof parsed === "object" && !Array.isArray(parsed)) {
|
|
5677
|
+
return parsed;
|
|
5678
|
+
}
|
|
5679
|
+
} catch {
|
|
5680
|
+
}
|
|
5681
|
+
throw new ConfigError("metadata must be a JSON object");
|
|
5682
|
+
}
|
|
5683
|
+
function validateState(state) {
|
|
5684
|
+
if (!state) return void 0;
|
|
5685
|
+
if (state === "active" || state === "inactive" || state === "archived") return state;
|
|
5686
|
+
throw new ConfigError("state must be active, inactive, or archived");
|
|
5687
|
+
}
|
|
5688
|
+
async function withClient(options) {
|
|
5689
|
+
const config = await loadConfig({ projectId: options.projectId });
|
|
5690
|
+
const client = createClient(config);
|
|
5691
|
+
return { client, projectId: client.projectId() };
|
|
5692
|
+
}
|
|
5693
|
+
function handleError(err) {
|
|
5694
|
+
if (err instanceof CliError) {
|
|
5695
|
+
console.error(chalk12.red(err.message));
|
|
5696
|
+
process.exit(1);
|
|
5697
|
+
}
|
|
5698
|
+
throw err;
|
|
5699
|
+
}
|
|
5700
|
+
async function principalsListCommand(options) {
|
|
5701
|
+
try {
|
|
5702
|
+
const { client, projectId } = await withClient(options);
|
|
5703
|
+
const principals2 = await client.principals(projectId);
|
|
5704
|
+
if (principals2.length === 0) {
|
|
5705
|
+
console.log(chalk12.gray("No principals found. Create one with `io principals create <external-id>`."));
|
|
5706
|
+
return;
|
|
5707
|
+
}
|
|
5708
|
+
for (const principal of principals2) {
|
|
5709
|
+
console.log(
|
|
5710
|
+
` ${principal.externalId} ${chalk12.gray(principal.id)} ${chalk12.gray(principal.state)}`
|
|
5711
|
+
);
|
|
5712
|
+
}
|
|
5713
|
+
} catch (err) {
|
|
5714
|
+
handleError(err);
|
|
5715
|
+
}
|
|
5716
|
+
}
|
|
5717
|
+
async function principalsCreateCommand(externalId, options) {
|
|
5718
|
+
try {
|
|
5719
|
+
const { client, projectId } = await withClient(options);
|
|
5720
|
+
const principal = await client.createPrincipal(projectId, {
|
|
5721
|
+
externalId,
|
|
5722
|
+
state: validateState(options.state),
|
|
5723
|
+
metadata: parseMetadata(options.metadata)
|
|
5724
|
+
});
|
|
5725
|
+
console.log(chalk12.green(`\u2713 Created principal: ${principal.externalId}`));
|
|
5726
|
+
console.log(chalk12.gray(` ID: ${principal.id}`));
|
|
5727
|
+
console.log(chalk12.gray(` Project: ${principal.projectId}`));
|
|
5728
|
+
console.log(chalk12.gray(` State: ${principal.state}`));
|
|
5729
|
+
} catch (err) {
|
|
5730
|
+
handleError(err);
|
|
5731
|
+
}
|
|
5732
|
+
}
|
|
5733
|
+
async function principalsUpdateCommand(id, options) {
|
|
5734
|
+
try {
|
|
5735
|
+
const { client, projectId } = await withClient(options);
|
|
5736
|
+
const input = {
|
|
5737
|
+
externalId: options.externalId,
|
|
5738
|
+
state: validateState(options.state),
|
|
5739
|
+
metadata: parseMetadata(options.metadata)
|
|
5740
|
+
};
|
|
5741
|
+
if (!input.externalId && !input.state && !input.metadata) {
|
|
5742
|
+
throw new ConfigError("nothing to update; pass --external-id, --state, or --metadata");
|
|
5743
|
+
}
|
|
5744
|
+
const principal = await client.updatePrincipal(projectId, id, input);
|
|
5745
|
+
if (!principal) {
|
|
5746
|
+
console.log(chalk12.yellow(`Principal "${id}" not found.`));
|
|
5747
|
+
return;
|
|
5748
|
+
}
|
|
5749
|
+
console.log(chalk12.green(`\u2713 Updated principal: ${principal.externalId}`));
|
|
5750
|
+
console.log(chalk12.gray(` ID: ${principal.id}`));
|
|
5751
|
+
console.log(chalk12.gray(` Project: ${principal.projectId}`));
|
|
5752
|
+
console.log(chalk12.gray(` State: ${principal.state}`));
|
|
5753
|
+
} catch (err) {
|
|
5754
|
+
handleError(err);
|
|
5755
|
+
}
|
|
5756
|
+
}
|
|
5757
|
+
async function principalsDeleteCommand(id, options) {
|
|
5758
|
+
try {
|
|
5759
|
+
const { client, projectId } = await withClient(options);
|
|
5760
|
+
const principal = await client.deletePrincipal(projectId, id);
|
|
5761
|
+
if (!principal) {
|
|
5762
|
+
console.log(chalk12.yellow(`Principal "${id}" not found.`));
|
|
5763
|
+
return;
|
|
5764
|
+
}
|
|
5765
|
+
console.log(chalk12.green(`\u2713 Deleted principal: ${principal.externalId}`));
|
|
5766
|
+
} catch (err) {
|
|
5767
|
+
handleError(err);
|
|
5768
|
+
}
|
|
5769
|
+
}
|
|
4267
5770
|
function meterBar(used, limit, width = 20) {
|
|
4268
|
-
if (limit === 0) return
|
|
5771
|
+
if (limit === 0) return chalk12.gray("\u2591".repeat(width));
|
|
4269
5772
|
const ratio = Math.min(used / limit, 1);
|
|
4270
5773
|
const filled = Math.round(ratio * width);
|
|
4271
5774
|
const empty = width - filled;
|
|
4272
|
-
const color = ratio >= 0.9 ?
|
|
4273
|
-
return color("\u2588".repeat(filled)) +
|
|
5775
|
+
const color = ratio >= 0.9 ? chalk12.red : ratio >= 0.7 ? chalk12.yellow : chalk12.green;
|
|
5776
|
+
return color("\u2588".repeat(filled)) + chalk12.gray("\u2591".repeat(empty));
|
|
4274
5777
|
}
|
|
4275
5778
|
function formatNumber(n) {
|
|
4276
5779
|
if (n >= 1e6) return `${(n / 1e6).toFixed(1)}M`;
|
|
@@ -4280,9 +5783,11 @@ function formatNumber(n) {
|
|
|
4280
5783
|
var METER_LABELS = {
|
|
4281
5784
|
events: "Events",
|
|
4282
5785
|
ingest_tokens: "Ingest Tokens",
|
|
5786
|
+
query_tokens: "Query Tokens",
|
|
4283
5787
|
voice_seconds: "Voice Seconds",
|
|
4284
5788
|
compute_gb_sec: "Compute (GB\xB7s)",
|
|
4285
|
-
storage_gb: "Storage (GB)"
|
|
5789
|
+
storage_gb: "Memory Storage (GB)",
|
|
5790
|
+
artifact_storage_gb: "Artifact Storage (GB)"
|
|
4286
5791
|
};
|
|
4287
5792
|
async function quotaCommand() {
|
|
4288
5793
|
try {
|
|
@@ -4290,29 +5795,30 @@ async function quotaCommand() {
|
|
|
4290
5795
|
const client = createClient(config);
|
|
4291
5796
|
const statuses = await client.quotaStatuses();
|
|
4292
5797
|
if (statuses.length === 0) {
|
|
4293
|
-
console.log(
|
|
5798
|
+
console.log(chalk12.gray("No quota data available."));
|
|
4294
5799
|
return;
|
|
4295
5800
|
}
|
|
4296
|
-
console.log(
|
|
5801
|
+
console.log(chalk12.bold("\nQuota Usage\n"));
|
|
4297
5802
|
for (const status of statuses) {
|
|
4298
5803
|
const label = METER_LABELS[status.meter] ?? status.meter;
|
|
4299
5804
|
const bar = meterBar(status.used, status.limit);
|
|
4300
5805
|
const pct = status.limit > 0 ? `${status.percentageUsed.toFixed(1)}%` : "\u2014";
|
|
4301
5806
|
const usage = `${formatNumber(status.used)} / ${formatNumber(status.limit)}`;
|
|
4302
|
-
const enforcement = status.enforcement === "hard_cap" ?
|
|
4303
|
-
console.log(` ${label.padEnd(
|
|
5807
|
+
const enforcement = status.enforcement === "hard_cap" ? chalk12.red("hard") : chalk12.yellow("soft");
|
|
5808
|
+
console.log(` ${label.padEnd(22)} ${bar} ${usage.padStart(16)} ${pct.padStart(6)} ${enforcement}`);
|
|
4304
5809
|
}
|
|
4305
5810
|
console.log();
|
|
4306
5811
|
} catch (err) {
|
|
4307
5812
|
if (err instanceof CliError) {
|
|
4308
|
-
console.error(
|
|
5813
|
+
console.error(chalk12.red(err.message));
|
|
4309
5814
|
process.exit(1);
|
|
4310
5815
|
}
|
|
4311
5816
|
throw err;
|
|
4312
5817
|
}
|
|
4313
5818
|
}
|
|
4314
|
-
|
|
4315
|
-
|
|
5819
|
+
|
|
5820
|
+
// src/api/subscription.ts
|
|
5821
|
+
var ACTIVITY_EVENT_SUBSCRIPTION = `
|
|
4316
5822
|
subscription ActivityEventReceived($projectId: ID!, $input: ActivityEventFilterInput) {
|
|
4317
5823
|
activityEventReceived(projectId: $projectId, input: $input) {
|
|
4318
5824
|
id
|
|
@@ -4333,56 +5839,6 @@ var ACTIVITY_SUBSCRIPTION = `
|
|
|
4333
5839
|
}
|
|
4334
5840
|
}
|
|
4335
5841
|
`;
|
|
4336
|
-
function buildActivityEventsSocketUrl(apiUrl) {
|
|
4337
|
-
const url = new URL(apiUrl);
|
|
4338
|
-
const basePath = url.pathname.replace(/\/+$/, "");
|
|
4339
|
-
const socketBase = basePath.endsWith("/graphql") ? basePath.slice(0, -"/graphql".length) : basePath;
|
|
4340
|
-
url.protocol = url.protocol === "https:" ? "wss:" : "ws:";
|
|
4341
|
-
url.pathname = `${socketBase === "/" ? "" : socketBase}/socket`;
|
|
4342
|
-
url.search = "";
|
|
4343
|
-
url.hash = "";
|
|
4344
|
-
return url.toString();
|
|
4345
|
-
}
|
|
4346
|
-
function subscribeToActivityEvents(options) {
|
|
4347
|
-
if (!options.credentials.apiKey) {
|
|
4348
|
-
throw new ConfigError(
|
|
4349
|
-
"Live activity subscriptions require API key authentication on the current io_beam socket contract."
|
|
4350
|
-
);
|
|
4351
|
-
}
|
|
4352
|
-
const phoenixSocket = new Socket(buildActivityEventsSocketUrl(options.apiUrl), {
|
|
4353
|
-
params: socketAuthParams(options.credentials),
|
|
4354
|
-
timeout: 1e4
|
|
4355
|
-
});
|
|
4356
|
-
const absintheSocket = AbsintheSocket.create(phoenixSocket);
|
|
4357
|
-
const notifier = AbsintheSocket.send(absintheSocket, {
|
|
4358
|
-
operation: ACTIVITY_SUBSCRIPTION,
|
|
4359
|
-
variables: { projectId: options.projectId, input: options.input }
|
|
4360
|
-
});
|
|
4361
|
-
const observer = {
|
|
4362
|
-
onAbort: (error) => {
|
|
4363
|
-
options.onError?.(error);
|
|
4364
|
-
},
|
|
4365
|
-
onCancel: () => {
|
|
4366
|
-
},
|
|
4367
|
-
onError: (error) => {
|
|
4368
|
-
options.onError?.(error);
|
|
4369
|
-
},
|
|
4370
|
-
onResult: (result) => {
|
|
4371
|
-
const event = result.data?.activityEventReceived;
|
|
4372
|
-
if (event) {
|
|
4373
|
-
options.onEvent(event);
|
|
4374
|
-
}
|
|
4375
|
-
},
|
|
4376
|
-
onStart: () => {
|
|
4377
|
-
options.onConnected?.();
|
|
4378
|
-
}
|
|
4379
|
-
};
|
|
4380
|
-
AbsintheSocket.observe(absintheSocket, notifier, observer);
|
|
4381
|
-
phoenixSocket.connect();
|
|
4382
|
-
return () => {
|
|
4383
|
-
phoenixSocket.disconnect();
|
|
4384
|
-
};
|
|
4385
|
-
}
|
|
4386
5842
|
|
|
4387
5843
|
// src/activity/presentation.ts
|
|
4388
5844
|
var DISPLAY_NAME_NAMESPACES = /* @__PURE__ */ new Set([
|
|
@@ -4701,18 +6157,17 @@ function inferActivityPrincipalLabel(event) {
|
|
|
4701
6157
|
|
|
4702
6158
|
// src/commands/logs.ts
|
|
4703
6159
|
var LEVEL_COLORS = {
|
|
4704
|
-
error:
|
|
4705
|
-
warning:
|
|
4706
|
-
info:
|
|
4707
|
-
success:
|
|
4708
|
-
scheduled:
|
|
6160
|
+
error: chalk12.red,
|
|
6161
|
+
warning: chalk12.yellow,
|
|
6162
|
+
info: chalk12.blue,
|
|
6163
|
+
success: chalk12.green,
|
|
6164
|
+
scheduled: chalk12.magenta
|
|
4709
6165
|
};
|
|
4710
6166
|
var FOLLOW_POLL_INTERVAL_MS = 2e3;
|
|
4711
|
-
var FOLLOW_SEEN_ID_LIMIT = 1e3;
|
|
4712
6167
|
var FOLLOW_BATCH_LIMIT = 200;
|
|
4713
6168
|
function colorLevel(level) {
|
|
4714
|
-
if (!level) return
|
|
4715
|
-
const colorFn = LEVEL_COLORS[level] ??
|
|
6169
|
+
if (!level) return chalk12.gray("\u2014");
|
|
6170
|
+
const colorFn = LEVEL_COLORS[level] ?? chalk12.gray;
|
|
4716
6171
|
return colorFn(level.padEnd(7));
|
|
4717
6172
|
}
|
|
4718
6173
|
function pad(value, width) {
|
|
@@ -4725,8 +6180,8 @@ function formatTime(isoString) {
|
|
|
4725
6180
|
}
|
|
4726
6181
|
function formatDuration(ms) {
|
|
4727
6182
|
if (ms == null) return "";
|
|
4728
|
-
if (ms < 1e3) return
|
|
4729
|
-
return
|
|
6183
|
+
if (ms < 1e3) return chalk12.gray(`${ms}ms`);
|
|
6184
|
+
return chalk12.gray(`${(ms / 1e3).toFixed(1)}s`);
|
|
4730
6185
|
}
|
|
4731
6186
|
function truncate(value, width) {
|
|
4732
6187
|
if (value.length <= width) return value;
|
|
@@ -4734,19 +6189,19 @@ function truncate(value, width) {
|
|
|
4734
6189
|
}
|
|
4735
6190
|
function formatHeader() {
|
|
4736
6191
|
return [
|
|
4737
|
-
` ${
|
|
4738
|
-
|
|
4739
|
-
|
|
4740
|
-
|
|
4741
|
-
|
|
4742
|
-
|
|
6192
|
+
` ${chalk12.gray(pad("TIME", 10))}`,
|
|
6193
|
+
chalk12.gray(pad("STATUS", 9)),
|
|
6194
|
+
chalk12.gray(pad("CATEGORY", 14)),
|
|
6195
|
+
chalk12.gray(pad("NAME", 24)),
|
|
6196
|
+
chalk12.gray(pad("DURATION", 10)),
|
|
6197
|
+
chalk12.gray("DETAIL")
|
|
4743
6198
|
].join(" ");
|
|
4744
6199
|
}
|
|
4745
6200
|
function formatEvent(event) {
|
|
4746
|
-
const time =
|
|
6201
|
+
const time = chalk12.gray(formatTime(event.occurredAt));
|
|
4747
6202
|
const statusKey = inferActivityEventStatus(event);
|
|
4748
6203
|
const status = colorLevel(statusKey);
|
|
4749
|
-
const category =
|
|
6204
|
+
const category = chalk12.cyan(pad(inferActivityEventCategory(event), 14));
|
|
4750
6205
|
const name = pad(truncate(inferActivityEventName(event), 24), 24);
|
|
4751
6206
|
const duration = formatDuration(event.durationMs);
|
|
4752
6207
|
const detail = inferActivityEventDetail(event) || inferActivityPrincipalLabel(event) || "";
|
|
@@ -4756,77 +6211,16 @@ function formatEvent(event) {
|
|
|
4756
6211
|
category,
|
|
4757
6212
|
name,
|
|
4758
6213
|
pad(duration || "", 10),
|
|
4759
|
-
detail ?
|
|
6214
|
+
detail ? chalk12.gray(detail) : ""
|
|
4760
6215
|
].join(" ").trimEnd();
|
|
4761
6216
|
}
|
|
4762
|
-
function
|
|
4763
|
-
return events.reduce((latest, event) => {
|
|
4764
|
-
return event.occurredAt > latest ? event.occurredAt : latest;
|
|
4765
|
-
}, fallback);
|
|
4766
|
-
}
|
|
4767
|
-
function trimSeenIds(seenIds, order) {
|
|
4768
|
-
while (order.length > FOLLOW_SEEN_ID_LIMIT) {
|
|
4769
|
-
const evicted = order.shift();
|
|
4770
|
-
if (evicted) {
|
|
4771
|
-
seenIds.delete(evicted);
|
|
4772
|
-
}
|
|
4773
|
-
}
|
|
4774
|
-
}
|
|
4775
|
-
function orderEvents(events) {
|
|
4776
|
-
return [...events].sort((left, right) => {
|
|
4777
|
-
if (left.occurredAt === right.occurredAt) {
|
|
4778
|
-
return left.id.localeCompare(right.id);
|
|
4779
|
-
}
|
|
4780
|
-
return left.occurredAt.localeCompare(right.occurredAt);
|
|
4781
|
-
});
|
|
4782
|
-
}
|
|
4783
|
-
function wait(ms) {
|
|
4784
|
-
return new Promise((resolve4) => {
|
|
4785
|
-
setTimeout(resolve4, ms);
|
|
4786
|
-
});
|
|
4787
|
-
}
|
|
4788
|
-
function followBatchInput(input, since) {
|
|
6217
|
+
function followPollInput(input, since) {
|
|
4789
6218
|
return {
|
|
4790
6219
|
...input,
|
|
4791
6220
|
since,
|
|
4792
6221
|
limit: Math.max(input?.limit ?? 0, FOLLOW_BATCH_LIMIT)
|
|
4793
6222
|
};
|
|
4794
6223
|
}
|
|
4795
|
-
async function drainFollowEvents(client, projectId, input, since, seenIds, seenOrder) {
|
|
4796
|
-
let cursor = since;
|
|
4797
|
-
const events = [];
|
|
4798
|
-
for (let i = 0; i < 10; i += 1) {
|
|
4799
|
-
const batch = orderEvents(
|
|
4800
|
-
await client.projectEvents(projectId, followBatchInput(input, cursor))
|
|
4801
|
-
);
|
|
4802
|
-
const unseen = batch.filter((event) => !seenIds.has(event.id));
|
|
4803
|
-
if (unseen.length === 0) {
|
|
4804
|
-
return { events, since: cursor };
|
|
4805
|
-
}
|
|
4806
|
-
events.push(...unseen);
|
|
4807
|
-
cursor = latestOccurredAt(unseen, cursor);
|
|
4808
|
-
for (const event of unseen) {
|
|
4809
|
-
seenIds.add(event.id);
|
|
4810
|
-
seenOrder.push(event.id);
|
|
4811
|
-
}
|
|
4812
|
-
trimSeenIds(seenIds, seenOrder);
|
|
4813
|
-
if (batch.length < FOLLOW_BATCH_LIMIT) {
|
|
4814
|
-
return { events, since: cursor };
|
|
4815
|
-
}
|
|
4816
|
-
}
|
|
4817
|
-
return { events, since: cursor };
|
|
4818
|
-
}
|
|
4819
|
-
function installSigintHandler(onExit) {
|
|
4820
|
-
const handleSigint = () => {
|
|
4821
|
-
console.log(chalk11.gray("\n Disconnected."));
|
|
4822
|
-
onExit();
|
|
4823
|
-
process.exit(0);
|
|
4824
|
-
};
|
|
4825
|
-
process.once("SIGINT", handleSigint);
|
|
4826
|
-
return () => {
|
|
4827
|
-
process.removeListener("SIGINT", handleSigint);
|
|
4828
|
-
};
|
|
4829
|
-
}
|
|
4830
6224
|
async function logsCommand(options) {
|
|
4831
6225
|
try {
|
|
4832
6226
|
const config = await loadConfig();
|
|
@@ -4842,18 +6236,18 @@ async function logsCommand(options) {
|
|
|
4842
6236
|
const since = parseSince(options.since);
|
|
4843
6237
|
if (since) input.since = since;
|
|
4844
6238
|
}
|
|
4845
|
-
const
|
|
4846
|
-
if (
|
|
4847
|
-
console.log(
|
|
6239
|
+
const initialEvents = await client.projectEvents(pid, input);
|
|
6240
|
+
if (initialEvents.length === 0 && !options.follow) {
|
|
6241
|
+
console.log(chalk12.gray("No events found."));
|
|
4848
6242
|
return;
|
|
4849
6243
|
}
|
|
4850
|
-
if (
|
|
4851
|
-
console.log(
|
|
4852
|
-
Activity Log \u2014 ${
|
|
6244
|
+
if (initialEvents.length > 0) {
|
|
6245
|
+
console.log(chalk12.bold(`
|
|
6246
|
+
Activity Log \u2014 ${initialEvents.length} events
|
|
4853
6247
|
`));
|
|
4854
6248
|
console.log(formatHeader());
|
|
4855
|
-
console.log(
|
|
4856
|
-
for (const event of
|
|
6249
|
+
console.log(chalk12.gray(` ${"\u2500".repeat(92)}`));
|
|
6250
|
+
for (const event of initialEvents) {
|
|
4857
6251
|
console.log(formatEvent(event));
|
|
4858
6252
|
}
|
|
4859
6253
|
}
|
|
@@ -4863,7 +6257,7 @@ Activity Log \u2014 ${events.length} events
|
|
|
4863
6257
|
client,
|
|
4864
6258
|
pid,
|
|
4865
6259
|
input,
|
|
4866
|
-
|
|
6260
|
+
initialEvents,
|
|
4867
6261
|
followStartedAt ?? (/* @__PURE__ */ new Date()).toISOString()
|
|
4868
6262
|
);
|
|
4869
6263
|
} else {
|
|
@@ -4871,7 +6265,7 @@ Activity Log \u2014 ${events.length} events
|
|
|
4871
6265
|
}
|
|
4872
6266
|
} catch (err) {
|
|
4873
6267
|
if (err instanceof CliError) {
|
|
4874
|
-
console.error(
|
|
6268
|
+
console.error(chalk12.red(err.message));
|
|
4875
6269
|
process.exit(1);
|
|
4876
6270
|
}
|
|
4877
6271
|
throw err;
|
|
@@ -4882,89 +6276,69 @@ async function tailEvents(apiUrl, client, projectId, input, initialEvents, follo
|
|
|
4882
6276
|
if (!creds || !creds.token) {
|
|
4883
6277
|
throw new ConfigError("Not authenticated. Run `io login` first.");
|
|
4884
6278
|
}
|
|
4885
|
-
console.log(
|
|
4886
|
-
|
|
4887
|
-
|
|
4888
|
-
|
|
4889
|
-
|
|
4890
|
-
|
|
4891
|
-
|
|
4892
|
-
|
|
4893
|
-
|
|
4894
|
-
|
|
4895
|
-
|
|
4896
|
-
|
|
4897
|
-
|
|
4898
|
-
|
|
6279
|
+
console.log(chalk12.gray("\n Waiting for events... (Ctrl+C to stop)\n"));
|
|
6280
|
+
const initialSince = initialEvents.reduce(
|
|
6281
|
+
(latest, event) => event.occurredAt > latest ? event.occurredAt : latest,
|
|
6282
|
+
followStartedAt
|
|
6283
|
+
);
|
|
6284
|
+
const socketClient = createSocketClient({ apiUrl, credentials: creds });
|
|
6285
|
+
const subscriptionVars = input;
|
|
6286
|
+
let liveStarted = false;
|
|
6287
|
+
try {
|
|
6288
|
+
await watch(
|
|
6289
|
+
{
|
|
6290
|
+
subscribe: (onEvent, onError, onConnected) => {
|
|
6291
|
+
const unsubscribe = socketClient.subscribe(
|
|
6292
|
+
ACTIVITY_EVENT_SUBSCRIPTION,
|
|
6293
|
+
{ projectId, input: subscriptionVars },
|
|
6294
|
+
{
|
|
6295
|
+
onStart: onConnected,
|
|
6296
|
+
onResult: (result) => {
|
|
6297
|
+
const event = result.data?.activityEventReceived;
|
|
6298
|
+
if (event) onEvent(event);
|
|
6299
|
+
},
|
|
6300
|
+
onError,
|
|
6301
|
+
onAbort: onError
|
|
6302
|
+
}
|
|
6303
|
+
);
|
|
6304
|
+
return { unsubscribe };
|
|
6305
|
+
},
|
|
6306
|
+
poll: async (since) => {
|
|
6307
|
+
return client.projectEvents(projectId, followPollInput(input, since));
|
|
6308
|
+
},
|
|
6309
|
+
pollIntervalMs: FOLLOW_POLL_INTERVAL_MS,
|
|
6310
|
+
initialSince,
|
|
6311
|
+
getId: (event) => event.id,
|
|
6312
|
+
getCursor: (event) => event.occurredAt,
|
|
6313
|
+
seenIdLimit: 1e3
|
|
6314
|
+
},
|
|
6315
|
+
{
|
|
4899
6316
|
onEvent: (event) => {
|
|
4900
|
-
if (
|
|
4901
|
-
|
|
6317
|
+
if (!liveStarted) {
|
|
6318
|
+
liveStarted = true;
|
|
4902
6319
|
}
|
|
4903
6320
|
console.log(formatEvent(event));
|
|
4904
|
-
seenIds2.add(event.id);
|
|
4905
|
-
seenOrder2.push(event.id);
|
|
4906
|
-
trimSeenIds(seenIds2, seenOrder2);
|
|
4907
6321
|
},
|
|
4908
|
-
onConnected: () => {
|
|
4909
|
-
|
|
4910
|
-
|
|
4911
|
-
|
|
4912
|
-
|
|
4913
|
-
|
|
4914
|
-
|
|
4915
|
-
|
|
4916
|
-
|
|
4917
|
-
|
|
4918
|
-
for (const event of events) {
|
|
4919
|
-
console.log(formatEvent(event));
|
|
4920
|
-
}
|
|
4921
|
-
}).catch((error) => {
|
|
4922
|
-
console.error(chalk11.red(`
|
|
4923
|
-
Connection error: ${error.message}`));
|
|
4924
|
-
removeSigintHandler2();
|
|
4925
|
-
disconnect?.();
|
|
4926
|
-
reject(error);
|
|
4927
|
-
});
|
|
6322
|
+
onConnected: (mode) => {
|
|
6323
|
+
if (mode === "subscription") {
|
|
6324
|
+
console.log(chalk12.green(" \u25CF Connected \u2014 tailing live events\n"));
|
|
6325
|
+
} else {
|
|
6326
|
+
console.log(
|
|
6327
|
+
chalk12.green(
|
|
6328
|
+
" \u25CF Connected \u2014 polling for live events with your authenticated session\n"
|
|
6329
|
+
)
|
|
6330
|
+
);
|
|
6331
|
+
}
|
|
4928
6332
|
},
|
|
4929
|
-
onError: (
|
|
4930
|
-
console.error(
|
|
4931
|
-
Connection error: ${
|
|
4932
|
-
removeSigintHandler2();
|
|
4933
|
-
disconnect?.();
|
|
4934
|
-
reject(error);
|
|
6333
|
+
onError: (err) => {
|
|
6334
|
+
console.error(chalk12.red(`
|
|
6335
|
+
Connection error: ${err.message}`));
|
|
4935
6336
|
}
|
|
4936
|
-
});
|
|
4937
|
-
});
|
|
4938
|
-
}
|
|
4939
|
-
console.log(
|
|
4940
|
-
chalk11.green(" \u25CF Connected \u2014 polling for live events with your authenticated session\n")
|
|
4941
|
-
);
|
|
4942
|
-
const seenIds = new Set(initialEvents.map((event) => event.id));
|
|
4943
|
-
const seenOrder = initialEvents.map((event) => event.id);
|
|
4944
|
-
let since = latestOccurredAt(initialEvents, followStartedAt);
|
|
4945
|
-
const removeSigintHandler = installSigintHandler(() => {
|
|
4946
|
-
});
|
|
4947
|
-
try {
|
|
4948
|
-
for (; ; ) {
|
|
4949
|
-
const batch = await drainFollowEvents(
|
|
4950
|
-
client,
|
|
4951
|
-
projectId,
|
|
4952
|
-
input,
|
|
4953
|
-
since,
|
|
4954
|
-
seenIds,
|
|
4955
|
-
seenOrder
|
|
4956
|
-
);
|
|
4957
|
-
const nextEvents = batch.events;
|
|
4958
|
-
for (const event of nextEvents) {
|
|
4959
|
-
console.log(formatEvent(event));
|
|
4960
|
-
}
|
|
4961
|
-
if (nextEvents.length > 0) {
|
|
4962
|
-
since = batch.since;
|
|
4963
6337
|
}
|
|
4964
|
-
|
|
4965
|
-
|
|
6338
|
+
);
|
|
6339
|
+
console.log(chalk12.gray("\n Disconnected."));
|
|
4966
6340
|
} finally {
|
|
4967
|
-
|
|
6341
|
+
socketClient.close();
|
|
4968
6342
|
}
|
|
4969
6343
|
}
|
|
4970
6344
|
function parseSince(value) {
|
|
@@ -4997,16 +6371,20 @@ async function signalEmitCommand(name, options) {
|
|
|
4997
6371
|
try {
|
|
4998
6372
|
const config = await loadConfig({ projectId: options.projectId });
|
|
4999
6373
|
const client = createClient(config);
|
|
5000
|
-
const signal =
|
|
6374
|
+
const signal = normalizeRuntimeSignalName(name);
|
|
5001
6375
|
if (!signal) {
|
|
5002
6376
|
throw new ConfigError(
|
|
5003
6377
|
"signal name must be a bare event name like smoke.start or use the signal: namespace"
|
|
5004
6378
|
);
|
|
5005
6379
|
}
|
|
6380
|
+
if (!options.principalId || options.principalId.trim() === "") {
|
|
6381
|
+
throw new ConfigError("signals emit requires --principal-id <id>");
|
|
6382
|
+
}
|
|
5006
6383
|
const payload = parseJsonObject(options.payload, "payload");
|
|
5007
6384
|
const metadata = parseJsonObject(options.metadata, "metadata");
|
|
5008
6385
|
const event = await client.emitSignal({
|
|
5009
6386
|
signal,
|
|
6387
|
+
principalId: options.principalId,
|
|
5010
6388
|
payload,
|
|
5011
6389
|
metadata,
|
|
5012
6390
|
correlationId: options.correlationId,
|
|
@@ -5018,18 +6396,266 @@ async function signalEmitCommand(name, options) {
|
|
|
5018
6396
|
console.log(JSON.stringify(event, null, 2));
|
|
5019
6397
|
return;
|
|
5020
6398
|
}
|
|
5021
|
-
console.log(
|
|
5022
|
-
console.log(
|
|
5023
|
-
console.log(
|
|
6399
|
+
console.log(chalk12.green(`\u2713 Emitted ${event.name}`));
|
|
6400
|
+
console.log(chalk12.gray(` Event: ${event.id}`));
|
|
6401
|
+
console.log(chalk12.gray(` Project: ${event.projectId}`));
|
|
5024
6402
|
if (event.dispatchStatus) {
|
|
5025
|
-
console.log(
|
|
6403
|
+
console.log(chalk12.gray(` Dispatch: ${event.dispatchStatus}`));
|
|
5026
6404
|
}
|
|
5027
6405
|
if (event.occurredAt) {
|
|
5028
|
-
console.log(
|
|
6406
|
+
console.log(chalk12.gray(` At: ${event.occurredAt}`));
|
|
6407
|
+
}
|
|
6408
|
+
} catch (err) {
|
|
6409
|
+
if (err instanceof CliError) {
|
|
6410
|
+
console.error(chalk12.red(err.message));
|
|
6411
|
+
process.exit(1);
|
|
6412
|
+
}
|
|
6413
|
+
throw err;
|
|
6414
|
+
}
|
|
6415
|
+
}
|
|
6416
|
+
var POLL_INTERVAL_MS = 2e3;
|
|
6417
|
+
var POLL_BATCH_LIMIT = 200;
|
|
6418
|
+
function formatEvent2(event) {
|
|
6419
|
+
const time = new Date(event.occurredAt).toLocaleTimeString("en-GB", {
|
|
6420
|
+
hour12: false
|
|
6421
|
+
});
|
|
6422
|
+
const status = inferActivityEventStatus(event);
|
|
6423
|
+
const category = inferActivityEventCategory(event);
|
|
6424
|
+
const name = inferActivityEventName(event);
|
|
6425
|
+
const detail = inferActivityEventDetail(event) ?? "";
|
|
6426
|
+
return ` ${chalk12.gray(time)} ${chalk12.cyan(category)} ${name}${detail ? ` ${chalk12.gray(detail)}` : ""}${status ? ` ${chalk12.gray(`(${status})`)}` : ""}`;
|
|
6427
|
+
}
|
|
6428
|
+
async function reconcileCommand(options) {
|
|
6429
|
+
try {
|
|
6430
|
+
const config = await loadConfig();
|
|
6431
|
+
const client = createClient(config);
|
|
6432
|
+
const projectId = options.projectId ?? client.projectId();
|
|
6433
|
+
console.log(chalk12.gray(`Requesting reconciliation for project ${projectId}\u2026`));
|
|
6434
|
+
const accepted = await client.reconcileProject(projectId);
|
|
6435
|
+
if (!accepted) {
|
|
6436
|
+
throw new CliError(
|
|
6437
|
+
"Runtime rejected the reconcile request.",
|
|
6438
|
+
"RECONCILE_REJECTED"
|
|
6439
|
+
);
|
|
6440
|
+
}
|
|
6441
|
+
console.log(chalk12.green("\u2713 Reconcile requested."));
|
|
6442
|
+
if (!options.watch) {
|
|
6443
|
+
return;
|
|
6444
|
+
}
|
|
6445
|
+
const creds = await loadCredentials();
|
|
6446
|
+
if (!creds?.token) {
|
|
6447
|
+
throw new ConfigError("Not authenticated. Run `io login` first.");
|
|
6448
|
+
}
|
|
6449
|
+
console.log(
|
|
6450
|
+
chalk12.gray("\nTailing activity for this project (Ctrl+C to stop)\u2026\n")
|
|
6451
|
+
);
|
|
6452
|
+
const socketClient = createSocketClient({
|
|
6453
|
+
apiUrl: config.apiUrl,
|
|
6454
|
+
credentials: creds
|
|
6455
|
+
});
|
|
6456
|
+
const initialSince = (/* @__PURE__ */ new Date()).toISOString();
|
|
6457
|
+
try {
|
|
6458
|
+
await watch(
|
|
6459
|
+
{
|
|
6460
|
+
subscribe: (onEvent, onError, onConnected) => {
|
|
6461
|
+
const unsubscribe = socketClient.subscribe(
|
|
6462
|
+
ACTIVITY_EVENT_SUBSCRIPTION,
|
|
6463
|
+
{ projectId, input: { limit: POLL_BATCH_LIMIT } },
|
|
6464
|
+
{
|
|
6465
|
+
onStart: onConnected,
|
|
6466
|
+
onResult: (result) => {
|
|
6467
|
+
const event = result.data?.activityEventReceived;
|
|
6468
|
+
if (event) onEvent(event);
|
|
6469
|
+
},
|
|
6470
|
+
onError,
|
|
6471
|
+
onAbort: onError
|
|
6472
|
+
}
|
|
6473
|
+
);
|
|
6474
|
+
return { unsubscribe };
|
|
6475
|
+
},
|
|
6476
|
+
poll: async (since) => {
|
|
6477
|
+
return client.projectEvents(projectId, {
|
|
6478
|
+
since,
|
|
6479
|
+
limit: POLL_BATCH_LIMIT
|
|
6480
|
+
});
|
|
6481
|
+
},
|
|
6482
|
+
pollIntervalMs: POLL_INTERVAL_MS,
|
|
6483
|
+
initialSince,
|
|
6484
|
+
getId: (event) => event.id,
|
|
6485
|
+
getCursor: (event) => event.occurredAt
|
|
6486
|
+
},
|
|
6487
|
+
{
|
|
6488
|
+
onEvent: (event) => console.log(formatEvent2(event)),
|
|
6489
|
+
onConnected: (mode) => {
|
|
6490
|
+
if (mode === "subscription") {
|
|
6491
|
+
console.log(chalk12.green(" \u25CF Connected \u2014 tailing live events\n"));
|
|
6492
|
+
} else {
|
|
6493
|
+
console.log(
|
|
6494
|
+
chalk12.green(
|
|
6495
|
+
" \u25CF Connected \u2014 polling for live events with your authenticated session\n"
|
|
6496
|
+
)
|
|
6497
|
+
);
|
|
6498
|
+
}
|
|
6499
|
+
}
|
|
6500
|
+
}
|
|
6501
|
+
);
|
|
6502
|
+
console.log(chalk12.gray("\nDisconnected."));
|
|
6503
|
+
} finally {
|
|
6504
|
+
socketClient.close();
|
|
6505
|
+
}
|
|
6506
|
+
} catch (err) {
|
|
6507
|
+
if (err instanceof CliError) {
|
|
6508
|
+
console.error(chalk12.red(err.message));
|
|
6509
|
+
process.exit(1);
|
|
6510
|
+
}
|
|
6511
|
+
throw err;
|
|
6512
|
+
}
|
|
6513
|
+
}
|
|
6514
|
+
function isRecord4(value) {
|
|
6515
|
+
return !!value && typeof value === "object" && !Array.isArray(value);
|
|
6516
|
+
}
|
|
6517
|
+
function isCatalogShape2(value) {
|
|
6518
|
+
if (!isRecord4(value)) return false;
|
|
6519
|
+
return value.contract_version === "io.invocation.v2" && value.snapshot_version === "io_runtime.snapshot.v2" && typeof value.catalog_hash === "string" && isRecord4(value.resources) && isRecord4(value.signals) && isRecord4(value.tools) && isRecord4(value.schemas);
|
|
6520
|
+
}
|
|
6521
|
+
async function readLocalCatalog(buildDir) {
|
|
6522
|
+
const path = resolve(process.cwd(), buildDir, "catalog.json");
|
|
6523
|
+
let raw;
|
|
6524
|
+
try {
|
|
6525
|
+
raw = await readFile(path, "utf-8");
|
|
6526
|
+
} catch (err) {
|
|
6527
|
+
if (err instanceof Error && "code" in err && err.code === "ENOENT") {
|
|
6528
|
+
throw new CliError(
|
|
6529
|
+
`Local catalog not found at ${path}. Run \`io build\` or \`io deploy\` first.`,
|
|
6530
|
+
"CATALOG_NOT_FOUND"
|
|
6531
|
+
);
|
|
6532
|
+
}
|
|
6533
|
+
throw err;
|
|
6534
|
+
}
|
|
6535
|
+
let parsed;
|
|
6536
|
+
try {
|
|
6537
|
+
parsed = JSON.parse(raw);
|
|
6538
|
+
} catch (err) {
|
|
6539
|
+
throw new CliError(
|
|
6540
|
+
`Local catalog at ${path} is not valid JSON: ${err instanceof Error ? err.message : String(err)}`,
|
|
6541
|
+
"CATALOG_INVALID_JSON"
|
|
6542
|
+
);
|
|
6543
|
+
}
|
|
6544
|
+
if (!isCatalogShape2(parsed)) {
|
|
6545
|
+
throw new CliError(
|
|
6546
|
+
`Local catalog at ${path} does not match the io.invocation.v2 catalog shape.`,
|
|
6547
|
+
"CATALOG_SHAPE_MISMATCH"
|
|
6548
|
+
);
|
|
6549
|
+
}
|
|
6550
|
+
return parsed;
|
|
6551
|
+
}
|
|
6552
|
+
function describeExecutor(executor) {
|
|
6553
|
+
switch (executor.kind) {
|
|
6554
|
+
case "platform_tool":
|
|
6555
|
+
return `${executor.provider_kind}.${executor.backend_kind}.${executor.operation}`;
|
|
6556
|
+
case "stream_publish":
|
|
6557
|
+
return `${executor.provider_kind}.${executor.backend_kind} \u2192 ${executor.destination_ref.slice(0, 19)}\u2026`;
|
|
6558
|
+
case "webhook_response":
|
|
6559
|
+
return "webhook response";
|
|
6560
|
+
case "http_request":
|
|
6561
|
+
return `${executor.method} ${executor.url}`;
|
|
6562
|
+
case "graphql_request":
|
|
6563
|
+
return `POST ${executor.endpoint}${executor.operation_name ? ` (${executor.operation_name})` : ""}`;
|
|
6564
|
+
case "http_operation":
|
|
6565
|
+
return `${executor.method} ${executor.base_url}${executor.path}`;
|
|
6566
|
+
case "graphql_operation":
|
|
6567
|
+
return `POST ${executor.endpoint}${executor.operation_name ? ` (${executor.operation_name})` : ""}`;
|
|
6568
|
+
case "unsupported":
|
|
6569
|
+
return chalk12.red(`unsupported (${executor.reason})`);
|
|
6570
|
+
}
|
|
6571
|
+
}
|
|
6572
|
+
function formatCatalog(catalog2, label) {
|
|
6573
|
+
const lines = [];
|
|
6574
|
+
lines.push(chalk12.bold(`
|
|
6575
|
+
Catalog (${label})`));
|
|
6576
|
+
lines.push(
|
|
6577
|
+
` ${chalk12.gray("contract_version:")} ${catalog2.contract_version}`
|
|
6578
|
+
);
|
|
6579
|
+
lines.push(
|
|
6580
|
+
` ${chalk12.gray("snapshot_version:")} ${catalog2.snapshot_version}`
|
|
6581
|
+
);
|
|
6582
|
+
lines.push(` ${chalk12.gray("catalog_hash: ")} ${catalog2.catalog_hash}`);
|
|
6583
|
+
lines.push("");
|
|
6584
|
+
lines.push(chalk12.bold(" Counts"));
|
|
6585
|
+
lines.push(` resources: ${Object.keys(catalog2.resources).length}`);
|
|
6586
|
+
lines.push(` signals: ${Object.keys(catalog2.signals).length}`);
|
|
6587
|
+
lines.push(` tools: ${Object.keys(catalog2.tools).length}`);
|
|
6588
|
+
lines.push(` schemas: ${Object.keys(catalog2.schemas).length}`);
|
|
6589
|
+
const resourceIds = Object.keys(catalog2.resources).sort();
|
|
6590
|
+
if (resourceIds.length > 0) {
|
|
6591
|
+
lines.push("");
|
|
6592
|
+
lines.push(chalk12.bold(" Resources"));
|
|
6593
|
+
for (const id of resourceIds) {
|
|
6594
|
+
const resource = catalog2.resources[id];
|
|
6595
|
+
lines.push(
|
|
6596
|
+
` ${chalk12.cyan(resource.id)} ${chalk12.gray(
|
|
6597
|
+
`${resource.resource_kind}/${resource.backend_kind} scope=${resource.binding_scope} cred=${resource.credential_source}`
|
|
6598
|
+
)}`
|
|
6599
|
+
);
|
|
6600
|
+
for (const signalId of resource.signals) {
|
|
6601
|
+
const signal = catalog2.signals[signalId];
|
|
6602
|
+
if (!signal) continue;
|
|
6603
|
+
lines.push(
|
|
6604
|
+
` ${chalk12.magenta("signal")} ${signal.name} ${chalk12.gray(
|
|
6605
|
+
`(${signal.mode}, scope=${signal.scope})`
|
|
6606
|
+
)}`
|
|
6607
|
+
);
|
|
6608
|
+
}
|
|
6609
|
+
for (const toolId of resource.tools) {
|
|
6610
|
+
const tool = catalog2.tools[toolId];
|
|
6611
|
+
if (!tool) continue;
|
|
6612
|
+
lines.push(
|
|
6613
|
+
` ${chalk12.yellow("tool ")} ${tool.name} ${chalk12.gray(
|
|
6614
|
+
`(${tool.effect}, audit=${tool.audit_requirement}) ${describeExecutor(tool.executor)}`
|
|
6615
|
+
)}`
|
|
6616
|
+
);
|
|
6617
|
+
}
|
|
6618
|
+
}
|
|
6619
|
+
}
|
|
6620
|
+
lines.push("");
|
|
6621
|
+
return lines.join("\n");
|
|
6622
|
+
}
|
|
6623
|
+
async function catalogShowCommand(options) {
|
|
6624
|
+
try {
|
|
6625
|
+
let catalog2;
|
|
6626
|
+
let label;
|
|
6627
|
+
if (options.remote) {
|
|
6628
|
+
const config = await loadConfig();
|
|
6629
|
+
if (!config.apiUrl || !config.projectId) {
|
|
6630
|
+
throw new CliError(
|
|
6631
|
+
"No apiUrl or projectId configured. Run `io login` and set a project first.",
|
|
6632
|
+
"CONFIG_MISSING"
|
|
6633
|
+
);
|
|
6634
|
+
}
|
|
6635
|
+
const client = createClient(config);
|
|
6636
|
+
const remote = await client.fetchRemoteCatalog(client.projectId());
|
|
6637
|
+
if (!remote) {
|
|
6638
|
+
console.log(
|
|
6639
|
+
chalk12.gray("No active deployment catalog for this project yet.")
|
|
6640
|
+
);
|
|
6641
|
+
return;
|
|
6642
|
+
}
|
|
6643
|
+
catalog2 = remote;
|
|
6644
|
+
label = `remote: project ${client.projectId()}`;
|
|
6645
|
+
} else {
|
|
6646
|
+
const buildDir = options.buildDir ?? ".io";
|
|
6647
|
+
catalog2 = await readLocalCatalog(buildDir);
|
|
6648
|
+
label = `local: ${join(buildDir, "catalog.json")}`;
|
|
6649
|
+
}
|
|
6650
|
+
if (options.json) {
|
|
6651
|
+
process.stdout.write(stableStringify(catalog2, 2));
|
|
6652
|
+
process.stdout.write("\n");
|
|
6653
|
+
return;
|
|
5029
6654
|
}
|
|
6655
|
+
process.stdout.write(formatCatalog(catalog2, label));
|
|
5030
6656
|
} catch (err) {
|
|
5031
6657
|
if (err instanceof CliError) {
|
|
5032
|
-
console.error(
|
|
6658
|
+
console.error(chalk12.red(err.message));
|
|
5033
6659
|
process.exit(1);
|
|
5034
6660
|
}
|
|
5035
6661
|
throw err;
|
|
@@ -5038,24 +6664,33 @@ async function signalEmitCommand(name, options) {
|
|
|
5038
6664
|
|
|
5039
6665
|
// src/index.ts
|
|
5040
6666
|
var program = new Command();
|
|
5041
|
-
program.name("io").description("IO CLI \u2014 build and deploy behaviors").version("0.1.
|
|
6667
|
+
program.name("io").description("IO CLI \u2014 build and deploy behaviors").version("0.1.16");
|
|
5042
6668
|
program.command("init").description("Initialize io.config.json for the current project").option("--yes", "Overwrite existing config without prompting").action(initCommand);
|
|
5043
6669
|
program.command("build").description("Build behaviors from the io/ directory").option("--dir <path>", "Source directory", "io").option("--minify", "Minify bundles", false).action(buildCommand);
|
|
5044
|
-
program.command("deploy").description("Build and package for deployment").option("--dir <path>", "Source directory", "io").option("--no-minify", "Skip minification").option("--yes", "Skip confirmation prompt").action(deployCommand);
|
|
6670
|
+
program.command("deploy").description("Build and package for deployment").option("--dir <path>", "Source directory", "io").option("--no-minify", "Skip minification").option("--yes", "Skip confirmation prompt").option("--watch", "Tail deployment status until it reaches a terminal state").action(deployCommand);
|
|
5045
6671
|
program.command("login").description("Authenticate with the IO platform").option("--token <value>", "API token for CI/CD environments").action(loginCommand);
|
|
5046
6672
|
program.command("logout").description("Remove stored credentials").action(logoutCommand);
|
|
5047
6673
|
program.command("whoami").description("Show current authentication status").action(whoamiCommand);
|
|
5048
6674
|
var projects = program.command("projects").description("Manage projects");
|
|
5049
6675
|
projects.command("list").description("List all projects").action(projectsListCommand);
|
|
5050
6676
|
projects.command("create <name>").description("Create a new project").action(projectsCreateCommand);
|
|
6677
|
+
projects.command("delete <id>").description("Delete a project (requires typing the project name)").option("--yes", "Skip the name confirmation prompt").option("--organization-id <id>", "Organization that owns the project").action(projectsDeleteCommand);
|
|
5051
6678
|
var secrets = program.command("secrets").description("Manage project secrets");
|
|
5052
6679
|
secrets.command("set <name> <value>").description("Set a secret value").option("--target <targets...>", "Restrict to specific targets").action(secretsSetCommand);
|
|
5053
6680
|
secrets.command("list").description("List all secrets").action(secretsListCommand);
|
|
5054
6681
|
secrets.command("remove <name>").description("Remove a secret").action(secretsRemoveCommand);
|
|
6682
|
+
var principals = program.command("principals").description("Manage project principals");
|
|
6683
|
+
principals.command("list").description("List principals in the active project").option("--project-id <id>", "Override the configured project ID").action(principalsListCommand);
|
|
6684
|
+
principals.command("create <external-id>").description("Create a principal in the active project").option("--project-id <id>", "Override the configured project ID").option("--state <state>", "Principal state: active, inactive, or archived").option("--metadata <json>", "Principal metadata as a JSON object").action(principalsCreateCommand);
|
|
6685
|
+
principals.command("update <id>").description("Update a principal").option("--project-id <id>", "Override the configured project ID").option("--external-id <external-id>", "Replace the external ID").option("--state <state>", "Principal state: active, inactive, or archived").option("--metadata <json>", "Replace principal metadata with a JSON object").action(principalsUpdateCommand);
|
|
6686
|
+
principals.command("delete <id>").description("Delete a principal").option("--project-id <id>", "Override the configured project ID").action(principalsDeleteCommand);
|
|
5055
6687
|
var signals = program.command("signals").description("Emit runtime signals");
|
|
5056
|
-
signals.command("emit <name>").description("Emit a signal into the active project's event stream").option("--payload <json>", "Signal payload as a JSON object", "{}").option("--metadata <json>", "Signal metadata as a JSON object", "{}").option("--project-id <id>", "Override the configured project ID").option("--correlation-id <id>", "Correlation ID to attach to the signal").option("--causation-id <id>", "Causation ID to attach to the signal").option("--idempotency-key <key>", "Optional idempotency key").option("--source-name <name>", "Optional source name for the emitted signal").option("--json", "Print the raw emitted event as JSON", false).action(signalEmitCommand);
|
|
6688
|
+
signals.command("emit <name>").description("Emit a signal into the active project's event stream").option("--payload <json>", "Signal payload as a JSON object", "{}").option("--metadata <json>", "Signal metadata as a JSON object", "{}").option("--project-id <id>", "Override the configured project ID").requiredOption("--principal-id <id>", "Project principal ID for the emitted signal").option("--correlation-id <id>", "Correlation ID to attach to the signal").option("--causation-id <id>", "Causation ID to attach to the signal").option("--idempotency-key <key>", "Optional idempotency key").option("--source-name <name>", "Optional source name for the emitted signal").option("--json", "Print the raw emitted event as JSON", false).action(signalEmitCommand);
|
|
5057
6689
|
program.command("quota").description("Show current quota usage for the active organization").action(quotaCommand);
|
|
5058
6690
|
program.command("logs").description("Show recent activity events").option("--limit <count>", "Number of events to show", "50").option("--type <types...>", "Filter by event types").option("--since <duration>", "Show events since (e.g. 15m, 1h, 7d)").option("--follow", "Tail events in real-time").action(logsCommand);
|
|
6691
|
+
program.command("reconcile").description("Force runtime to reconcile resources for the active project").option("--project-id <id>", "Override the configured project ID").option("--watch", "Tail activity events after the reconcile request").action(reconcileCommand);
|
|
6692
|
+
var catalog = program.command("catalog").description("Inspect the v2 deployment catalog");
|
|
6693
|
+
catalog.command("show").description("Print the local or remote deployment catalog").option("--remote", "Show the active deployment catalog from runtime").option("--json", "Print the raw catalog JSON").option("--build-dir <path>", "Local build directory", ".io").action(catalogShowCommand);
|
|
5059
6694
|
program.parse();
|
|
5060
6695
|
//# sourceMappingURL=index.js.map
|
|
5061
6696
|
//# sourceMappingURL=index.js.map
|