@eve-horizon/cli 0.2.20 → 0.2.21
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 +2290 -424
- package/package.json +7 -7
package/dist/index.js
CHANGED
|
@@ -9811,8 +9811,8 @@ var require_sync = __commonJS({
|
|
|
9811
9811
|
}
|
|
9812
9812
|
return x;
|
|
9813
9813
|
};
|
|
9814
|
-
var defaultReadPackageSync = function defaultReadPackageSync2(
|
|
9815
|
-
var body =
|
|
9814
|
+
var defaultReadPackageSync = function defaultReadPackageSync2(readFileSync21, pkgfile) {
|
|
9815
|
+
var body = readFileSync21(pkgfile);
|
|
9816
9816
|
try {
|
|
9817
9817
|
var pkg = JSON.parse(body);
|
|
9818
9818
|
return pkg;
|
|
@@ -9832,7 +9832,7 @@ var require_sync = __commonJS({
|
|
|
9832
9832
|
}
|
|
9833
9833
|
var opts = normalizeOptions(x, options);
|
|
9834
9834
|
var isFile = opts.isFile || defaultIsFile;
|
|
9835
|
-
var
|
|
9835
|
+
var readFileSync21 = opts.readFileSync || fs4.readFileSync;
|
|
9836
9836
|
var isDirectory = opts.isDirectory || defaultIsDir;
|
|
9837
9837
|
var realpathSync = opts.realpathSync || defaultRealpathSync;
|
|
9838
9838
|
var readPackageSync = opts.readPackageSync || defaultReadPackageSync;
|
|
@@ -9889,7 +9889,7 @@ var require_sync = __commonJS({
|
|
|
9889
9889
|
if (!isFile(pkgfile)) {
|
|
9890
9890
|
return loadpkg(path6.dirname(dir));
|
|
9891
9891
|
}
|
|
9892
|
-
var pkg = readPackageSync(
|
|
9892
|
+
var pkg = readPackageSync(readFileSync21, pkgfile);
|
|
9893
9893
|
if (pkg && opts.packageFilter) {
|
|
9894
9894
|
pkg = opts.packageFilter(
|
|
9895
9895
|
pkg,
|
|
@@ -9903,7 +9903,7 @@ var require_sync = __commonJS({
|
|
|
9903
9903
|
var pkgfile = path6.join(maybeRealpathSync(realpathSync, x2, opts), "/package.json");
|
|
9904
9904
|
if (isFile(pkgfile)) {
|
|
9905
9905
|
try {
|
|
9906
|
-
var pkg = readPackageSync(
|
|
9906
|
+
var pkg = readPackageSync(readFileSync21, pkgfile);
|
|
9907
9907
|
} catch (e) {
|
|
9908
9908
|
}
|
|
9909
9909
|
if (pkg && opts.packageFilter) {
|
|
@@ -13197,8 +13197,8 @@ var require_utils3 = __commonJS({
|
|
|
13197
13197
|
const connectOptions = url;
|
|
13198
13198
|
const protocol = getProtocol(connectOptions === null || connectOptions === void 0 ? void 0 : connectOptions.protocol);
|
|
13199
13199
|
Object.assign(attributes, Object.assign({}, extractConnectionAttributeOrLog(url, semantic_conventions_1.SEMATTRS_MESSAGING_PROTOCOL, protocol, "protocol")));
|
|
13200
|
-
const
|
|
13201
|
-
Object.assign(attributes, Object.assign({}, extractConnectionAttributeOrLog(url, semantic_conventions_1.SEMATTRS_NET_PEER_NAME,
|
|
13200
|
+
const hostname2 = getHostname(connectOptions === null || connectOptions === void 0 ? void 0 : connectOptions.hostname);
|
|
13201
|
+
Object.assign(attributes, Object.assign({}, extractConnectionAttributeOrLog(url, semantic_conventions_1.SEMATTRS_NET_PEER_NAME, hostname2, "hostname")));
|
|
13202
13202
|
const port = getPort(connectOptions.port, protocol);
|
|
13203
13203
|
Object.assign(attributes, Object.assign({}, extractConnectionAttributeOrLog(url, semantic_conventions_1.SEMATTRS_NET_PEER_PORT, port, "port")));
|
|
13204
13204
|
} else {
|
|
@@ -13208,8 +13208,8 @@ var require_utils3 = __commonJS({
|
|
|
13208
13208
|
const urlParts = new URL(censoredUrl);
|
|
13209
13209
|
const protocol = getProtocol(urlParts.protocol);
|
|
13210
13210
|
Object.assign(attributes, Object.assign({}, extractConnectionAttributeOrLog(censoredUrl, semantic_conventions_1.SEMATTRS_MESSAGING_PROTOCOL, protocol, "protocol")));
|
|
13211
|
-
const
|
|
13212
|
-
Object.assign(attributes, Object.assign({}, extractConnectionAttributeOrLog(censoredUrl, semantic_conventions_1.SEMATTRS_NET_PEER_NAME,
|
|
13211
|
+
const hostname2 = getHostname(urlParts.hostname);
|
|
13212
|
+
Object.assign(attributes, Object.assign({}, extractConnectionAttributeOrLog(censoredUrl, semantic_conventions_1.SEMATTRS_NET_PEER_NAME, hostname2, "hostname")));
|
|
13213
13213
|
const port = getPort(urlParts.port ? parseInt(urlParts.port) : void 0, protocol);
|
|
13214
13214
|
Object.assign(attributes, Object.assign({}, extractConnectionAttributeOrLog(censoredUrl, semantic_conventions_1.SEMATTRS_NET_PEER_PORT, port, "port")));
|
|
13215
13215
|
} catch (err) {
|
|
@@ -19754,7 +19754,7 @@ var require_OpenTelemetryBunyanStream = __commonJS({
|
|
|
19754
19754
|
msg,
|
|
19755
19755
|
v,
|
|
19756
19756
|
// eslint-disable-line @typescript-eslint/no-unused-vars
|
|
19757
|
-
hostname,
|
|
19757
|
+
hostname: hostname2,
|
|
19758
19758
|
// eslint-disable-line @typescript-eslint/no-unused-vars
|
|
19759
19759
|
pid,
|
|
19760
19760
|
// eslint-disable-line @typescript-eslint/no-unused-vars
|
|
@@ -21171,9 +21171,9 @@ var require_instrumentation9 = __commonJS({
|
|
|
21171
21171
|
*/
|
|
21172
21172
|
_getPatchLookupFunction(original) {
|
|
21173
21173
|
const plugin = this;
|
|
21174
|
-
return function patchedLookup(
|
|
21175
|
-
if (utils.isIgnored(
|
|
21176
|
-
return original.apply(this, [
|
|
21174
|
+
return function patchedLookup(hostname2, ...args) {
|
|
21175
|
+
if (utils.isIgnored(hostname2, plugin.getConfig().ignoreHostnames, (e) => api_1.diag.error("caught ignoreHostname error: ", e))) {
|
|
21176
|
+
return original.apply(this, [hostname2, ...args]);
|
|
21177
21177
|
}
|
|
21178
21178
|
const argsCount = args.length;
|
|
21179
21179
|
api_1.diag.debug("wrap lookup callback function and starts span");
|
|
@@ -21184,7 +21184,7 @@ var require_instrumentation9 = __commonJS({
|
|
|
21184
21184
|
const originalCallback = args[argsCount - 1];
|
|
21185
21185
|
if (typeof originalCallback === "function") {
|
|
21186
21186
|
args[argsCount - 1] = plugin._wrapLookupCallback(originalCallback, span);
|
|
21187
|
-
return (0, instrumentation_1.safeExecuteInTheMiddle)(() => original.apply(this, [
|
|
21187
|
+
return (0, instrumentation_1.safeExecuteInTheMiddle)(() => original.apply(this, [hostname2, ...args]), (error) => {
|
|
21188
21188
|
if (error != null) {
|
|
21189
21189
|
utils.setError(error, span);
|
|
21190
21190
|
span.end();
|
|
@@ -21192,7 +21192,7 @@ var require_instrumentation9 = __commonJS({
|
|
|
21192
21192
|
});
|
|
21193
21193
|
} else {
|
|
21194
21194
|
const promise = (0, instrumentation_1.safeExecuteInTheMiddle)(() => original.apply(this, [
|
|
21195
|
-
|
|
21195
|
+
hostname2,
|
|
21196
21196
|
...args
|
|
21197
21197
|
]), (error) => {
|
|
21198
21198
|
if (error != null) {
|
|
@@ -28558,8 +28558,8 @@ var require_utils14 = __commonJS({
|
|
|
28558
28558
|
if (!pathname && optionsParsed.path) {
|
|
28559
28559
|
pathname = url.parse(optionsParsed.path).pathname || "/";
|
|
28560
28560
|
}
|
|
28561
|
-
const
|
|
28562
|
-
origin = `${optionsParsed.protocol || "http:"}//${
|
|
28561
|
+
const hostname2 = optionsParsed.host || (optionsParsed.port != null ? `${optionsParsed.hostname}${optionsParsed.port}` : optionsParsed.hostname);
|
|
28562
|
+
origin = `${optionsParsed.protocol || "http:"}//${hostname2}`;
|
|
28563
28563
|
}
|
|
28564
28564
|
const method = optionsParsed.method ? optionsParsed.method.toUpperCase() : "GET";
|
|
28565
28565
|
return { origin, pathname, method, optionsParsed };
|
|
@@ -28579,7 +28579,7 @@ var require_utils14 = __commonJS({
|
|
|
28579
28579
|
return { hostname: requestOptions.hostname, port: requestOptions.port };
|
|
28580
28580
|
}
|
|
28581
28581
|
const matches = ((_a = requestOptions.host) === null || _a === void 0 ? void 0 : _a.match(/^([^:/ ]+)(:\d{1,5})?/)) || null;
|
|
28582
|
-
const
|
|
28582
|
+
const hostname2 = requestOptions.hostname || (matches === null ? "localhost" : matches[1]);
|
|
28583
28583
|
let port = requestOptions.port;
|
|
28584
28584
|
if (!port) {
|
|
28585
28585
|
if (matches && matches[2]) {
|
|
@@ -28588,12 +28588,12 @@ var require_utils14 = __commonJS({
|
|
|
28588
28588
|
port = requestOptions.protocol === "https:" ? "443" : "80";
|
|
28589
28589
|
}
|
|
28590
28590
|
}
|
|
28591
|
-
return { hostname, port };
|
|
28591
|
+
return { hostname: hostname2, port };
|
|
28592
28592
|
};
|
|
28593
28593
|
exports2.extractHostnameAndPort = extractHostnameAndPort;
|
|
28594
28594
|
var getOutgoingRequestAttributes = (requestOptions, options, semconvStability) => {
|
|
28595
28595
|
var _a, _b;
|
|
28596
|
-
const
|
|
28596
|
+
const hostname2 = options.hostname;
|
|
28597
28597
|
const port = options.port;
|
|
28598
28598
|
const method = (_a = requestOptions.method) !== null && _a !== void 0 ? _a : "GET";
|
|
28599
28599
|
const normalizedMethod = normalizeMethod(method);
|
|
@@ -28604,13 +28604,13 @@ var require_utils14 = __commonJS({
|
|
|
28604
28604
|
[semantic_conventions_1.SEMATTRS_HTTP_URL]: urlFull,
|
|
28605
28605
|
[semantic_conventions_1.SEMATTRS_HTTP_METHOD]: method,
|
|
28606
28606
|
[semantic_conventions_1.SEMATTRS_HTTP_TARGET]: requestOptions.path || "/",
|
|
28607
|
-
[semantic_conventions_1.SEMATTRS_NET_PEER_NAME]:
|
|
28608
|
-
[semantic_conventions_1.SEMATTRS_HTTP_HOST]: (_b = headers.host) !== null && _b !== void 0 ? _b : `${
|
|
28607
|
+
[semantic_conventions_1.SEMATTRS_NET_PEER_NAME]: hostname2,
|
|
28608
|
+
[semantic_conventions_1.SEMATTRS_HTTP_HOST]: (_b = headers.host) !== null && _b !== void 0 ? _b : `${hostname2}:${port}`
|
|
28609
28609
|
};
|
|
28610
28610
|
const newAttributes = {
|
|
28611
28611
|
// Required attributes
|
|
28612
28612
|
[semantic_conventions_1.ATTR_HTTP_REQUEST_METHOD]: normalizedMethod,
|
|
28613
|
-
[semantic_conventions_1.ATTR_SERVER_ADDRESS]:
|
|
28613
|
+
[semantic_conventions_1.ATTR_SERVER_ADDRESS]: hostname2,
|
|
28614
28614
|
[semantic_conventions_1.ATTR_SERVER_PORT]: Number(port),
|
|
28615
28615
|
[semantic_conventions_1.ATTR_URL_FULL]: urlFull
|
|
28616
28616
|
// leaving out protocol version, it is not yet negotiated
|
|
@@ -28785,7 +28785,7 @@ var require_utils14 = __commonJS({
|
|
|
28785
28785
|
const httpVersion = request.httpVersion;
|
|
28786
28786
|
const requestUrl = request.url ? url.parse(request.url) : null;
|
|
28787
28787
|
const host = (requestUrl === null || requestUrl === void 0 ? void 0 : requestUrl.host) || headers.host;
|
|
28788
|
-
const
|
|
28788
|
+
const hostname2 = (requestUrl === null || requestUrl === void 0 ? void 0 : requestUrl.hostname) || (host === null || host === void 0 ? void 0 : host.replace(/^(.*)(:[0-9]{1,5})/, "$1")) || "localhost";
|
|
28789
28789
|
const method = request.method;
|
|
28790
28790
|
const normalizedMethod = normalizeMethod(method);
|
|
28791
28791
|
const serverAddress = getServerAddress(request, options.component);
|
|
@@ -28815,7 +28815,7 @@ var require_utils14 = __commonJS({
|
|
|
28815
28815
|
const oldAttributes = {
|
|
28816
28816
|
[semantic_conventions_1.SEMATTRS_HTTP_URL]: (0, exports2.getAbsoluteUrl)(requestUrl, headers, `${options.component}:`),
|
|
28817
28817
|
[semantic_conventions_1.SEMATTRS_HTTP_HOST]: host,
|
|
28818
|
-
[semantic_conventions_1.SEMATTRS_NET_HOST_NAME]:
|
|
28818
|
+
[semantic_conventions_1.SEMATTRS_NET_HOST_NAME]: hostname2,
|
|
28819
28819
|
[semantic_conventions_1.SEMATTRS_HTTP_METHOD]: method,
|
|
28820
28820
|
[semantic_conventions_1.SEMATTRS_HTTP_SCHEME]: options.component
|
|
28821
28821
|
};
|
|
@@ -29368,11 +29368,11 @@ var require_http = __commonJS({
|
|
|
29368
29368
|
}, true)) {
|
|
29369
29369
|
return original.apply(this, [optionsParsed, ...args]);
|
|
29370
29370
|
}
|
|
29371
|
-
const { hostname, port } = (0, utils_1.extractHostnameAndPort)(optionsParsed);
|
|
29371
|
+
const { hostname: hostname2, port } = (0, utils_1.extractHostnameAndPort)(optionsParsed);
|
|
29372
29372
|
const attributes = (0, utils_1.getOutgoingRequestAttributes)(optionsParsed, {
|
|
29373
29373
|
component,
|
|
29374
29374
|
port,
|
|
29375
|
-
hostname,
|
|
29375
|
+
hostname: hostname2,
|
|
29376
29376
|
hookAttributes: instrumentation._callStartSpanHook(optionsParsed, instrumentation.getConfig().startOutgoingSpanHook)
|
|
29377
29377
|
}, instrumentation._semconvStability);
|
|
29378
29378
|
const startTime = (0, core_1.hrTime)();
|
|
@@ -36012,7 +36012,7 @@ var require_log_sending_utils = __commonJS({
|
|
|
36012
36012
|
// depends on the user using the OpenTelemetry HostDetector and
|
|
36013
36013
|
// ProcessDetector.
|
|
36014
36014
|
// https://getpino.io/#/docs/api?id=opt-base
|
|
36015
|
-
hostname,
|
|
36015
|
+
hostname: hostname2,
|
|
36016
36016
|
// eslint-disable-line @typescript-eslint/no-unused-vars
|
|
36017
36017
|
pid,
|
|
36018
36018
|
// eslint-disable-line @typescript-eslint/no-unused-vars
|
|
@@ -39634,7 +39634,7 @@ var require_AlibabaCloudEcsDetector = __commonJS({
|
|
|
39634
39634
|
/** Gets identity and host info and returns them as attribs. Empty object if fails */
|
|
39635
39635
|
async _getAttributes(_config) {
|
|
39636
39636
|
const { "owner-account-id": accountId, "instance-id": instanceId, "instance-type": instanceType, "region-id": region, "zone-id": availabilityZone } = await this._fetchIdentity();
|
|
39637
|
-
const
|
|
39637
|
+
const hostname2 = await this._fetchHost();
|
|
39638
39638
|
return {
|
|
39639
39639
|
[semantic_conventions_1.SEMRESATTRS_CLOUD_PROVIDER]: semantic_conventions_1.CLOUDPROVIDERVALUES_ALIBABA_CLOUD,
|
|
39640
39640
|
[semantic_conventions_1.SEMRESATTRS_CLOUD_PLATFORM]: semantic_conventions_1.CLOUDPLATFORMVALUES_ALIBABA_CLOUD_ECS,
|
|
@@ -39643,7 +39643,7 @@ var require_AlibabaCloudEcsDetector = __commonJS({
|
|
|
39643
39643
|
[semantic_conventions_1.SEMRESATTRS_CLOUD_AVAILABILITY_ZONE]: availabilityZone,
|
|
39644
39644
|
[semantic_conventions_1.SEMRESATTRS_HOST_ID]: instanceId,
|
|
39645
39645
|
[semantic_conventions_1.SEMRESATTRS_HOST_TYPE]: instanceType,
|
|
39646
|
-
[semantic_conventions_1.SEMRESATTRS_HOST_NAME]:
|
|
39646
|
+
[semantic_conventions_1.SEMRESATTRS_HOST_NAME]: hostname2
|
|
39647
39647
|
};
|
|
39648
39648
|
}
|
|
39649
39649
|
/**
|
|
@@ -39888,7 +39888,7 @@ var require_AwsEc2DetectorSync = __commonJS({
|
|
|
39888
39888
|
try {
|
|
39889
39889
|
const token = await this._fetchToken();
|
|
39890
39890
|
const { accountId, instanceId, instanceType, region, availabilityZone } = await this._fetchIdentity(token);
|
|
39891
|
-
const
|
|
39891
|
+
const hostname2 = await this._fetchHost(token);
|
|
39892
39892
|
return {
|
|
39893
39893
|
[semconv_1.ATTR_CLOUD_PROVIDER]: semconv_1.CLOUD_PROVIDER_VALUE_AWS,
|
|
39894
39894
|
[semconv_1.ATTR_CLOUD_PLATFORM]: semconv_1.CLOUD_PLATFORM_VALUE_AWS_EC2,
|
|
@@ -39897,7 +39897,7 @@ var require_AwsEc2DetectorSync = __commonJS({
|
|
|
39897
39897
|
[semconv_1.ATTR_CLOUD_AVAILABILITY_ZONE]: availabilityZone,
|
|
39898
39898
|
[semconv_1.ATTR_HOST_ID]: instanceId,
|
|
39899
39899
|
[semconv_1.ATTR_HOST_TYPE]: instanceType,
|
|
39900
|
-
[semconv_1.ATTR_HOST_NAME]:
|
|
39900
|
+
[semconv_1.ATTR_HOST_NAME]: hostname2
|
|
39901
39901
|
};
|
|
39902
39902
|
} catch (_a) {
|
|
39903
39903
|
return {};
|
|
@@ -47946,7 +47946,7 @@ var require_GcpDetector = __commonJS({
|
|
|
47946
47946
|
api_1.diag.debug("GcpDetector failed: GCP Metadata unavailable.");
|
|
47947
47947
|
return {};
|
|
47948
47948
|
}
|
|
47949
|
-
const [projectId, instanceId, zoneId, clusterName,
|
|
47949
|
+
const [projectId, instanceId, zoneId, clusterName, hostname2] = await Promise.all([
|
|
47950
47950
|
this._getProjectId(),
|
|
47951
47951
|
this._getInstanceId(),
|
|
47952
47952
|
this._getZone(),
|
|
@@ -47956,7 +47956,7 @@ var require_GcpDetector = __commonJS({
|
|
|
47956
47956
|
const attributes = {};
|
|
47957
47957
|
attributes[semantic_conventions_1.SEMRESATTRS_CLOUD_ACCOUNT_ID] = projectId;
|
|
47958
47958
|
attributes[semantic_conventions_1.SEMRESATTRS_HOST_ID] = instanceId;
|
|
47959
|
-
attributes[semantic_conventions_1.SEMRESATTRS_HOST_NAME] =
|
|
47959
|
+
attributes[semantic_conventions_1.SEMRESATTRS_HOST_NAME] = hostname2;
|
|
47960
47960
|
attributes[semantic_conventions_1.SEMRESATTRS_CLOUD_AVAILABILITY_ZONE] = zoneId;
|
|
47961
47961
|
attributes[semantic_conventions_1.SEMRESATTRS_CLOUD_PROVIDER] = semantic_conventions_1.CLOUDPROVIDERVALUES_GCP;
|
|
47962
47962
|
if ((0, core_2.getEnv)().KUBERNETES_SERVICE_HOST)
|
|
@@ -48734,6 +48734,23 @@ function resolveContext(flags, credentials) {
|
|
|
48734
48734
|
profileSource
|
|
48735
48735
|
};
|
|
48736
48736
|
}
|
|
48737
|
+
function resolveContextForProfile(profileName, profile, credentials) {
|
|
48738
|
+
const apiUrl = profile.api_url || DEFAULT_API_URL;
|
|
48739
|
+
const authKey = toAuthKey(apiUrl);
|
|
48740
|
+
const tokenEntry = credentials.tokens[authKey] || credentials.profiles?.[profileName];
|
|
48741
|
+
return {
|
|
48742
|
+
apiUrl,
|
|
48743
|
+
orgId: profile.org_id,
|
|
48744
|
+
projectId: profile.project_id,
|
|
48745
|
+
profileName,
|
|
48746
|
+
profile,
|
|
48747
|
+
authKey,
|
|
48748
|
+
token: tokenEntry?.access_token,
|
|
48749
|
+
refreshToken: tokenEntry?.refresh_token,
|
|
48750
|
+
expiresAt: tokenEntry?.expires_at,
|
|
48751
|
+
profileSource: "local"
|
|
48752
|
+
};
|
|
48753
|
+
}
|
|
48737
48754
|
function normalizeRepoProfiles(parsed) {
|
|
48738
48755
|
if (!parsed || typeof parsed !== "object") {
|
|
48739
48756
|
return { profiles: {} };
|
|
@@ -48884,6 +48901,20 @@ var HELP = {
|
|
|
48884
48901
|
"eve project sync --path ./custom-manifest.yaml"
|
|
48885
48902
|
]
|
|
48886
48903
|
},
|
|
48904
|
+
status: {
|
|
48905
|
+
description: "Show deployment status across all profiles with service URLs",
|
|
48906
|
+
usage: "eve project status [--profile <name>] [--env <name>] [--json]",
|
|
48907
|
+
options: [
|
|
48908
|
+
"--profile <name> Show only this profile",
|
|
48909
|
+
"--env <name> Show only this environment",
|
|
48910
|
+
"--json Machine-readable JSON output"
|
|
48911
|
+
],
|
|
48912
|
+
examples: [
|
|
48913
|
+
"eve project status",
|
|
48914
|
+
"eve project status --profile staging",
|
|
48915
|
+
"eve project status --env sandbox --json"
|
|
48916
|
+
]
|
|
48917
|
+
},
|
|
48887
48918
|
members: {
|
|
48888
48919
|
description: "Manage project members (list, add, remove)",
|
|
48889
48920
|
usage: "eve project members [list|add|remove] [options]",
|
|
@@ -49619,34 +49650,63 @@ for cloud deployments. Credentials are stored globally per API URL.`,
|
|
|
49619
49650
|
subcommands: {
|
|
49620
49651
|
can: {
|
|
49621
49652
|
description: "Check if a principal can perform an action",
|
|
49622
|
-
usage: "eve access can --org <org_id> --user <
|
|
49653
|
+
usage: "eve access can --org <org_id> (--user <id>|--service-principal <id>|--group <id>) --permission <perm> [--project <project_id>] [--resource-type <type> --resource <id> --action <read|write|admin>]",
|
|
49623
49654
|
options: [
|
|
49624
49655
|
"--org <org_id> Org scope (uses profile default if omitted)",
|
|
49625
|
-
"--user <user_id> User to check (mutually exclusive with --service-principal)",
|
|
49626
|
-
"--service-principal <sp_id> Service principal to check (mutually exclusive with --user)",
|
|
49656
|
+
"--user <user_id> User to check (mutually exclusive with --service-principal/--group)",
|
|
49657
|
+
"--service-principal <sp_id> Service principal to check (mutually exclusive with --user/--group)",
|
|
49658
|
+
"--group <group_id> Group to check directly (mutually exclusive with --user/--service-principal)",
|
|
49627
49659
|
"--permission <perm> Permission to check (e.g., chat:write, jobs:admin)",
|
|
49628
|
-
"--project <project_id> Optional project scope for the check"
|
|
49660
|
+
"--project <project_id> Optional project scope for the check",
|
|
49661
|
+
"--resource-type <type> Optional resource type: orgfs|orgdocs|envdb",
|
|
49662
|
+
"--resource <id> Optional resource id/path (required when --resource-type used)",
|
|
49663
|
+
"--action <action> Optional action: read|write|admin"
|
|
49629
49664
|
],
|
|
49630
49665
|
examples: [
|
|
49631
49666
|
"eve access can --org org_xxx --user user_abc --permission chat:write",
|
|
49632
49667
|
"eve access can --org org_xxx --user user_abc --project proj_xxx --permission jobs:admin",
|
|
49633
|
-
"eve access can --org org_xxx --service-principal sp_xxx --permission jobs:read"
|
|
49668
|
+
"eve access can --org org_xxx --service-principal sp_xxx --permission jobs:read",
|
|
49669
|
+
"eve access can --org org_xxx --user user_abc --permission orgfs:read --resource-type orgfs --resource /groups/pm/spec.md --action read"
|
|
49634
49670
|
]
|
|
49635
49671
|
},
|
|
49636
49672
|
explain: {
|
|
49637
49673
|
description: "Explain the permission resolution chain",
|
|
49638
|
-
usage: "eve access explain --org <org_id> --user <
|
|
49674
|
+
usage: "eve access explain --org <org_id> (--user <id>|--service-principal <id>|--group <id>) --permission <perm> [--project <project_id>] [--resource-type <type> --resource <id> --action <read|write|admin>]",
|
|
49639
49675
|
options: [
|
|
49640
49676
|
"--org <org_id> Org scope (uses profile default if omitted)",
|
|
49641
|
-
"--user <user_id> User to explain (mutually exclusive with --service-principal)",
|
|
49642
|
-
"--service-principal <sp_id> Service principal to explain (mutually exclusive with --user)",
|
|
49677
|
+
"--user <user_id> User to explain (mutually exclusive with --service-principal/--group)",
|
|
49678
|
+
"--service-principal <sp_id> Service principal to explain (mutually exclusive with --user/--group)",
|
|
49679
|
+
"--group <group_id> Group to explain directly (mutually exclusive with --user/--service-principal)",
|
|
49643
49680
|
"--permission <perm> Permission to explain (e.g., chat:write, jobs:admin)",
|
|
49644
|
-
"--project <project_id> Optional project scope for the explanation"
|
|
49681
|
+
"--project <project_id> Optional project scope for the explanation",
|
|
49682
|
+
"--resource-type <type> Optional resource type: orgfs|orgdocs|envdb",
|
|
49683
|
+
"--resource <id> Optional resource id/path (required when --resource-type used)",
|
|
49684
|
+
"--action <action> Optional action: read|write|admin"
|
|
49645
49685
|
],
|
|
49646
49686
|
examples: [
|
|
49647
49687
|
"eve access explain --org org_xxx --user user_abc --permission jobs:admin",
|
|
49648
49688
|
"eve access explain --org org_xxx --user user_abc --project proj_xxx --permission jobs:admin",
|
|
49649
|
-
"eve access explain --org org_xxx --service-principal sp_xxx --permission jobs:read"
|
|
49689
|
+
"eve access explain --org org_xxx --service-principal sp_xxx --permission jobs:read",
|
|
49690
|
+
"eve access explain --org org_xxx --user user_abc --permission orgfs:read --resource-type orgfs --resource /groups/pm/spec.md --action read"
|
|
49691
|
+
]
|
|
49692
|
+
},
|
|
49693
|
+
groups: {
|
|
49694
|
+
description: "Manage access groups and members",
|
|
49695
|
+
usage: "eve access groups <create|list|show|update|delete|members> [args]",
|
|
49696
|
+
examples: [
|
|
49697
|
+
'eve access groups create "Product Management" --org org_xxx --slug pm-team',
|
|
49698
|
+
"eve access groups list --org org_xxx",
|
|
49699
|
+
"eve access groups members add pm-team --org org_xxx --user user_abc",
|
|
49700
|
+
"eve access groups members list pm-team --org org_xxx"
|
|
49701
|
+
]
|
|
49702
|
+
},
|
|
49703
|
+
memberships: {
|
|
49704
|
+
description: "Inspect memberships, effective bindings, and effective scopes for a principal",
|
|
49705
|
+
usage: "eve access memberships --org <org_id> (--user <id>|--service-principal <id>|--group <id>)",
|
|
49706
|
+
examples: [
|
|
49707
|
+
"eve access memberships --org org_xxx --user user_abc",
|
|
49708
|
+
"eve access memberships --org org_xxx --service-principal sp_abc",
|
|
49709
|
+
"eve access memberships --org org_xxx --group grp_abc"
|
|
49650
49710
|
]
|
|
49651
49711
|
},
|
|
49652
49712
|
validate: {
|
|
@@ -49904,10 +49964,15 @@ for cloud deployments. Credentials are stored globally per API URL.`,
|
|
|
49904
49964
|
examples: ["eve db schema --env staging"]
|
|
49905
49965
|
},
|
|
49906
49966
|
rls: {
|
|
49907
|
-
description: "Show RLS policies
|
|
49908
|
-
usage: "eve db rls --env <name> [--project <id>]",
|
|
49909
|
-
options: [
|
|
49910
|
-
|
|
49967
|
+
description: "Show RLS policies or scaffold helper SQL",
|
|
49968
|
+
usage: "eve db rls --env <name> [--project <id>] | eve db rls init --with-groups [--out <path>] [--force]",
|
|
49969
|
+
options: [
|
|
49970
|
+
"--env <name> Environment name (required for inspect mode)",
|
|
49971
|
+
"--with-groups Generate app.current_group_ids()/app.has_group() helper SQL (init mode)",
|
|
49972
|
+
"--out <path> Output file for init mode (default: db/rls/helpers.sql)",
|
|
49973
|
+
"--force Overwrite output file when it already exists"
|
|
49974
|
+
],
|
|
49975
|
+
examples: ["eve db rls --env staging", "eve db rls init --with-groups"]
|
|
49911
49976
|
},
|
|
49912
49977
|
sql: {
|
|
49913
49978
|
description: "Run parameterized SQL as the calling user",
|
|
@@ -50663,6 +50728,33 @@ eve-new-project-setup skill to complete configuration.`,
|
|
|
50663
50728
|
'eve docs query --org org_xxx --where "metadata.feature_status in draft,review"'
|
|
50664
50729
|
]
|
|
50665
50730
|
},
|
|
50731
|
+
fs: {
|
|
50732
|
+
description: "Manage org filesystem sync links, events, and diagnostics.",
|
|
50733
|
+
usage: "eve fs sync <subcommand> [options]",
|
|
50734
|
+
subcommands: {
|
|
50735
|
+
sync: {
|
|
50736
|
+
description: "Org filesystem sync operations",
|
|
50737
|
+
usage: "eve fs sync <init|status|logs|pause|resume|disconnect|mode|conflicts|resolve|doctor> [options]",
|
|
50738
|
+
options: [
|
|
50739
|
+
"init: --org <id> --local <path> [--mode two-way|push-only|pull-only]",
|
|
50740
|
+
"status: --org <id>",
|
|
50741
|
+
"logs: --org <id> [--after <seq>] [--limit <n>] [--follow]",
|
|
50742
|
+
"pause|resume|disconnect: --org <id> [--link <link_id>]",
|
|
50743
|
+
"mode: --org <id> --set <two-way|push-only|pull-only> [--link <link_id>]",
|
|
50744
|
+
"conflicts: --org <id> [--open-only]",
|
|
50745
|
+
"resolve: --org <id> --conflict <id> --strategy <pick-remote|pick-local|manual>",
|
|
50746
|
+
"doctor: --org <id>"
|
|
50747
|
+
]
|
|
50748
|
+
}
|
|
50749
|
+
},
|
|
50750
|
+
examples: [
|
|
50751
|
+
"eve fs sync init --org org_xxx --local ~/Eve/acme --mode two-way",
|
|
50752
|
+
"eve fs sync status --org org_xxx",
|
|
50753
|
+
"eve fs sync logs --org org_xxx --follow",
|
|
50754
|
+
"eve fs sync mode --org org_xxx --set pull-only",
|
|
50755
|
+
"eve fs sync doctor --org org_xxx"
|
|
50756
|
+
]
|
|
50757
|
+
},
|
|
50666
50758
|
resources: {
|
|
50667
50759
|
description: "Resolve resource URIs into content snapshots.",
|
|
50668
50760
|
usage: "eve resources <subcommand> [options]",
|
|
@@ -50951,6 +51043,7 @@ function showMainHelp() {
|
|
|
50951
51043
|
console.log(" workflow Inspect manifest workflows (list, show)");
|
|
50952
51044
|
console.log(" event Emit and inspect events (app integration)");
|
|
50953
51045
|
console.log(" docs Manage org documents");
|
|
51046
|
+
console.log(" fs Manage org filesystem sync");
|
|
50954
51047
|
console.log(" resources Resolve org/job resources");
|
|
50955
51048
|
console.log(" secrets Manage secrets (project/org/user scope)");
|
|
50956
51049
|
console.log(" harness Inspect harnesses and auth status");
|
|
@@ -51666,8 +51759,175 @@ async function handleProject(subcommand, positionals, flags, context2) {
|
|
|
51666
51759
|
}
|
|
51667
51760
|
return;
|
|
51668
51761
|
}
|
|
51762
|
+
case "status":
|
|
51763
|
+
return handleStatus(flags, context2, json);
|
|
51669
51764
|
default:
|
|
51670
|
-
throw new Error("Usage: eve project <ensure|list|get|spend|update|sync|members|bootstrap>");
|
|
51765
|
+
throw new Error("Usage: eve project <ensure|list|get|spend|update|sync|members|bootstrap|status>");
|
|
51766
|
+
}
|
|
51767
|
+
}
|
|
51768
|
+
async function handleStatus(flags, currentContext, json) {
|
|
51769
|
+
const repoProfiles = loadRepoProfiles();
|
|
51770
|
+
const credentials = loadCredentials();
|
|
51771
|
+
const profileFilter = getStringFlag(flags, ["profile"]);
|
|
51772
|
+
const envFilter = getStringFlag(flags, ["env"]);
|
|
51773
|
+
const profileEntries = Object.entries(repoProfiles.profiles);
|
|
51774
|
+
if (profileEntries.length === 0) {
|
|
51775
|
+
throw new Error(
|
|
51776
|
+
"No profiles configured. Use `eve profile create <name> --api-url <url> --project <id>` to add one."
|
|
51777
|
+
);
|
|
51778
|
+
}
|
|
51779
|
+
const results = [];
|
|
51780
|
+
for (const [name, profile] of profileEntries) {
|
|
51781
|
+
if (profileFilter && name !== profileFilter) continue;
|
|
51782
|
+
const active = name === repoProfiles.activeProfile;
|
|
51783
|
+
const ctx = resolveContextForProfile(name, profile, credentials);
|
|
51784
|
+
if (!ctx.projectId) {
|
|
51785
|
+
results.push({ name, active, api_url: ctx.apiUrl, error: "No project_id configured" });
|
|
51786
|
+
continue;
|
|
51787
|
+
}
|
|
51788
|
+
if (!ctx.token) {
|
|
51789
|
+
results.push({
|
|
51790
|
+
name,
|
|
51791
|
+
active,
|
|
51792
|
+
api_url: ctx.apiUrl,
|
|
51793
|
+
project_id: ctx.projectId,
|
|
51794
|
+
error: "Not authenticated (run: eve auth login --profile " + name + ")"
|
|
51795
|
+
});
|
|
51796
|
+
continue;
|
|
51797
|
+
}
|
|
51798
|
+
try {
|
|
51799
|
+
const result = await fetchProfileStatus(ctx, envFilter);
|
|
51800
|
+
results.push({ name, active, ...result });
|
|
51801
|
+
} catch (err) {
|
|
51802
|
+
results.push({
|
|
51803
|
+
name,
|
|
51804
|
+
active,
|
|
51805
|
+
api_url: ctx.apiUrl,
|
|
51806
|
+
project_id: ctx.projectId,
|
|
51807
|
+
error: err instanceof Error ? err.message : String(err)
|
|
51808
|
+
});
|
|
51809
|
+
}
|
|
51810
|
+
}
|
|
51811
|
+
if (json) {
|
|
51812
|
+
outputJson({ profiles: results }, json);
|
|
51813
|
+
return;
|
|
51814
|
+
}
|
|
51815
|
+
formatStatusOutput(results);
|
|
51816
|
+
}
|
|
51817
|
+
async function fetchProfileStatus(ctx, envFilter) {
|
|
51818
|
+
const project = await requestJson(ctx, `/projects/${ctx.projectId}`);
|
|
51819
|
+
const envResponse = await requestJson(ctx, `/projects/${ctx.projectId}/envs?limit=50`);
|
|
51820
|
+
const domain = inferDomain(ctx.apiUrl);
|
|
51821
|
+
const environments = [];
|
|
51822
|
+
for (const env of envResponse.data) {
|
|
51823
|
+
if (envFilter && env.name !== envFilter) continue;
|
|
51824
|
+
const status = env.suspended_at ? "suspended" : "active";
|
|
51825
|
+
if (env.suspended_at) {
|
|
51826
|
+
environments.push({
|
|
51827
|
+
name: env.name,
|
|
51828
|
+
type: env.type,
|
|
51829
|
+
status,
|
|
51830
|
+
namespace: env.namespace,
|
|
51831
|
+
services: []
|
|
51832
|
+
});
|
|
51833
|
+
continue;
|
|
51834
|
+
}
|
|
51835
|
+
let services = [];
|
|
51836
|
+
try {
|
|
51837
|
+
const diagnose = await requestJson(ctx, `/projects/${ctx.projectId}/envs/${env.name}/diagnose`);
|
|
51838
|
+
services = buildStatusServices(diagnose.pods, diagnose.namespace ?? env.namespace, domain);
|
|
51839
|
+
} catch {
|
|
51840
|
+
}
|
|
51841
|
+
environments.push({
|
|
51842
|
+
name: env.name,
|
|
51843
|
+
type: env.type,
|
|
51844
|
+
status,
|
|
51845
|
+
namespace: env.namespace,
|
|
51846
|
+
services
|
|
51847
|
+
});
|
|
51848
|
+
}
|
|
51849
|
+
return {
|
|
51850
|
+
api_url: ctx.apiUrl,
|
|
51851
|
+
project_id: project.id,
|
|
51852
|
+
project_name: project.name,
|
|
51853
|
+
environments
|
|
51854
|
+
};
|
|
51855
|
+
}
|
|
51856
|
+
function buildStatusServices(pods, namespace, domain) {
|
|
51857
|
+
const services = /* @__PURE__ */ new Map();
|
|
51858
|
+
for (const pod of pods) {
|
|
51859
|
+
const component = pod.labels["eve.component"] || pod.labels["app.kubernetes.io/name"] || pod.labels["app"] || pod.labels["component"] || "unknown";
|
|
51860
|
+
const existing = services.get(component) ?? { ready: 0, total: 0, phases: /* @__PURE__ */ new Set() };
|
|
51861
|
+
existing.total += 1;
|
|
51862
|
+
if (pod.ready) existing.ready += 1;
|
|
51863
|
+
existing.phases.add(pod.phase);
|
|
51864
|
+
services.set(component, existing);
|
|
51865
|
+
}
|
|
51866
|
+
return Array.from(services.entries()).sort(([a], [b]) => a.localeCompare(b)).map(([name, info]) => {
|
|
51867
|
+
const allDone = info.phases.size > 0 && [...info.phases].every((p) => p === "Succeeded" || p === "Failed");
|
|
51868
|
+
const status = allDone ? "completed" : info.ready === info.total ? "ready" : "not-ready";
|
|
51869
|
+
const url = !allDone && namespace && domain ? buildServiceUrl(name, namespace, domain) : null;
|
|
51870
|
+
return {
|
|
51871
|
+
name,
|
|
51872
|
+
pods_ready: info.ready,
|
|
51873
|
+
pods_total: info.total,
|
|
51874
|
+
status,
|
|
51875
|
+
url
|
|
51876
|
+
};
|
|
51877
|
+
});
|
|
51878
|
+
}
|
|
51879
|
+
function inferDomain(apiUrl) {
|
|
51880
|
+
try {
|
|
51881
|
+
const host = new URL(apiUrl).hostname;
|
|
51882
|
+
if (host.startsWith("api.eve.")) return host.slice("api.eve.".length);
|
|
51883
|
+
if (host.startsWith("api.")) return host.slice("api.".length);
|
|
51884
|
+
return null;
|
|
51885
|
+
} catch {
|
|
51886
|
+
return null;
|
|
51887
|
+
}
|
|
51888
|
+
}
|
|
51889
|
+
function buildServiceUrl(component, namespace, domain) {
|
|
51890
|
+
const slug = namespace.startsWith("eve-") ? namespace.slice(4) : namespace;
|
|
51891
|
+
const secure = !domain.includes("lvh.me") && !domain.includes("localhost");
|
|
51892
|
+
return `${secure ? "https" : "http"}://${component}.${slug}.${domain}`;
|
|
51893
|
+
}
|
|
51894
|
+
function formatStatusOutput(results) {
|
|
51895
|
+
for (let i = 0; i < results.length; i++) {
|
|
51896
|
+
const r = results[i];
|
|
51897
|
+
if (i > 0) console.log("");
|
|
51898
|
+
const marker = r.active ? " (active)" : "";
|
|
51899
|
+
console.log(`${r.name}${marker} ${r.api_url}`);
|
|
51900
|
+
if (r.error) {
|
|
51901
|
+
console.log(` error: ${r.error}`);
|
|
51902
|
+
continue;
|
|
51903
|
+
}
|
|
51904
|
+
const label = r.project_name ? `${r.project_id} (${r.project_name})` : r.project_id;
|
|
51905
|
+
console.log(` project: ${label}`);
|
|
51906
|
+
if (!r.environments || r.environments.length === 0) {
|
|
51907
|
+
console.log(" (no environments)");
|
|
51908
|
+
continue;
|
|
51909
|
+
}
|
|
51910
|
+
for (const env of r.environments) {
|
|
51911
|
+
console.log("");
|
|
51912
|
+
console.log(` ${env.name} ${env.status} ${env.type}`);
|
|
51913
|
+
if (env.services.length === 0) {
|
|
51914
|
+
if (env.status === "suspended") {
|
|
51915
|
+
console.log(" (suspended)");
|
|
51916
|
+
} else {
|
|
51917
|
+
console.log(" (no services)");
|
|
51918
|
+
}
|
|
51919
|
+
continue;
|
|
51920
|
+
}
|
|
51921
|
+
const nameW = Math.max(...env.services.map((s) => s.name.length));
|
|
51922
|
+
const podsW = Math.max(...env.services.map((s) => `${s.pods_ready}/${s.pods_total}`.length));
|
|
51923
|
+
for (const svc of env.services) {
|
|
51924
|
+
const pods = `${svc.pods_ready}/${svc.pods_total}`;
|
|
51925
|
+
const urlPart = svc.url ? ` ${svc.url}` : "";
|
|
51926
|
+
console.log(
|
|
51927
|
+
` ${padRight(svc.name, nameW)} ${padRight(pods, podsW)} ${padRight(svc.status, 9)}${urlPart}`
|
|
51928
|
+
);
|
|
51929
|
+
}
|
|
51930
|
+
}
|
|
51671
51931
|
}
|
|
51672
51932
|
}
|
|
51673
51933
|
function parseSinceValue2(since) {
|
|
@@ -51697,6 +51957,9 @@ function parseSinceValue2(since) {
|
|
|
51697
51957
|
}
|
|
51698
51958
|
return now.toISOString();
|
|
51699
51959
|
}
|
|
51960
|
+
function padRight(str, width) {
|
|
51961
|
+
return str.length >= width ? str : str + " ".repeat(width - str.length);
|
|
51962
|
+
}
|
|
51700
51963
|
function buildQuery2(params) {
|
|
51701
51964
|
const search = new URLSearchParams();
|
|
51702
51965
|
Object.entries(params).forEach(([key, value]) => {
|
|
@@ -55834,6 +56097,8 @@ var configSchema = external_exports.object({
|
|
|
55834
56097
|
EVE_GITHUB_TOKEN: external_exports.string().optional(),
|
|
55835
56098
|
EVE_SLACK_SIGNING_SECRET: external_exports.string().optional(),
|
|
55836
56099
|
EVE_INTERNAL_API_KEY: external_exports.string().optional(),
|
|
56100
|
+
EVE_ORG_FS_LINK_TOKEN_SECRET: external_exports.string().optional(),
|
|
56101
|
+
EVE_ORG_FS_LINK_TOKEN_TTL_SECONDS: external_exports.coerce.number().int().min(60).default(900),
|
|
55837
56102
|
EVE_SECRETS_MASTER_KEY: external_exports.string().optional(),
|
|
55838
56103
|
EVE_DEFAULT_DOMAIN: external_exports.string().optional(),
|
|
55839
56104
|
// Cluster-level default domain for Ingress (e.g., lvh.me, apps.example.com)
|
|
@@ -56961,56 +57226,179 @@ var AccessRoleResponseSchema = external_exports.object({
|
|
|
56961
57226
|
updated_at: external_exports.string()
|
|
56962
57227
|
});
|
|
56963
57228
|
var AccessRoleListResponseSchema = createApiListResponseSchema(AccessRoleResponseSchema);
|
|
57229
|
+
var AccessPrincipalTypeSchema = external_exports.enum(["user", "service_principal", "group"]);
|
|
57230
|
+
var AccessGroupMemberPrincipalTypeSchema = external_exports.enum(["user", "service_principal"]);
|
|
57231
|
+
var AccessScopePrefixesSchema = external_exports.object({
|
|
57232
|
+
allow_prefixes: external_exports.array(external_exports.string()).optional(),
|
|
57233
|
+
read_only_prefixes: external_exports.array(external_exports.string()).optional()
|
|
57234
|
+
}).strict();
|
|
57235
|
+
var AccessScopeEnvDbSchema = external_exports.object({
|
|
57236
|
+
schemas: external_exports.array(external_exports.string()).optional(),
|
|
57237
|
+
tables: external_exports.array(external_exports.string()).optional()
|
|
57238
|
+
}).strict();
|
|
57239
|
+
var AccessBindingScopeSchema = external_exports.object({
|
|
57240
|
+
orgfs: AccessScopePrefixesSchema.optional(),
|
|
57241
|
+
orgdocs: AccessScopePrefixesSchema.optional(),
|
|
57242
|
+
envdb: AccessScopeEnvDbSchema.optional()
|
|
57243
|
+
}).strict();
|
|
56964
57244
|
var CreateAccessBindingRequestSchema = external_exports.object({
|
|
56965
57245
|
role_name: external_exports.string().min(1),
|
|
56966
|
-
principal_type:
|
|
57246
|
+
principal_type: AccessPrincipalTypeSchema,
|
|
56967
57247
|
principal_id: external_exports.string().min(1),
|
|
56968
|
-
project_id: external_exports.string().optional()
|
|
57248
|
+
project_id: external_exports.string().optional(),
|
|
57249
|
+
scope_json: AccessBindingScopeSchema.optional()
|
|
56969
57250
|
});
|
|
56970
57251
|
var AccessBindingResponseSchema = external_exports.object({
|
|
56971
57252
|
id: external_exports.string(),
|
|
56972
57253
|
role_id: external_exports.string(),
|
|
56973
57254
|
role_name: external_exports.string(),
|
|
56974
|
-
principal_type:
|
|
57255
|
+
principal_type: AccessPrincipalTypeSchema,
|
|
56975
57256
|
principal_id: external_exports.string(),
|
|
56976
57257
|
project_id: external_exports.string().nullable(),
|
|
57258
|
+
scope_json: AccessBindingScopeSchema.nullable().optional(),
|
|
56977
57259
|
created_by: external_exports.string().nullable(),
|
|
56978
57260
|
created_at: external_exports.string()
|
|
56979
57261
|
});
|
|
56980
57262
|
var AccessBindingListResponseSchema = createApiListResponseSchema(AccessBindingResponseSchema);
|
|
57263
|
+
var CreateAccessGroupRequestSchema = external_exports.object({
|
|
57264
|
+
name: external_exports.string().min(1).max(100),
|
|
57265
|
+
slug: external_exports.string().min(1).max(100).regex(/^[a-z0-9][a-z0-9-_]*$/).optional(),
|
|
57266
|
+
description: external_exports.string().max(500).optional()
|
|
57267
|
+
});
|
|
57268
|
+
var UpdateAccessGroupRequestSchema = external_exports.object({
|
|
57269
|
+
name: external_exports.string().min(1).max(100).optional(),
|
|
57270
|
+
slug: external_exports.string().min(1).max(100).regex(/^[a-z0-9][a-z0-9-_]*$/).optional(),
|
|
57271
|
+
description: external_exports.string().max(500).nullable().optional()
|
|
57272
|
+
}).refine((value) => Object.keys(value).length > 0, {
|
|
57273
|
+
message: "At least one field is required"
|
|
57274
|
+
});
|
|
57275
|
+
var AccessGroupResponseSchema = external_exports.object({
|
|
57276
|
+
id: external_exports.string(),
|
|
57277
|
+
org_id: external_exports.string(),
|
|
57278
|
+
name: external_exports.string(),
|
|
57279
|
+
slug: external_exports.string(),
|
|
57280
|
+
description: external_exports.string().nullable(),
|
|
57281
|
+
created_by: external_exports.string().nullable(),
|
|
57282
|
+
created_at: external_exports.string(),
|
|
57283
|
+
updated_at: external_exports.string()
|
|
57284
|
+
});
|
|
57285
|
+
var AccessGroupListResponseSchema = createApiListResponseSchema(AccessGroupResponseSchema);
|
|
57286
|
+
var CreateAccessGroupMemberRequestSchema = external_exports.object({
|
|
57287
|
+
principal_type: AccessGroupMemberPrincipalTypeSchema,
|
|
57288
|
+
principal_id: external_exports.string().min(1)
|
|
57289
|
+
});
|
|
57290
|
+
var AccessGroupMemberResponseSchema = external_exports.object({
|
|
57291
|
+
group_id: external_exports.string(),
|
|
57292
|
+
principal_type: AccessGroupMemberPrincipalTypeSchema,
|
|
57293
|
+
principal_id: external_exports.string(),
|
|
57294
|
+
added_by: external_exports.string().nullable(),
|
|
57295
|
+
created_at: external_exports.string()
|
|
57296
|
+
});
|
|
57297
|
+
var AccessGroupMemberListResponseSchema = createApiListResponseSchema(AccessGroupMemberResponseSchema);
|
|
56981
57298
|
var AccessCanResponseSchema = external_exports.object({
|
|
56982
57299
|
allowed: external_exports.boolean(),
|
|
56983
|
-
source: external_exports.string()
|
|
57300
|
+
source: external_exports.string(),
|
|
57301
|
+
resource: external_exports.object({
|
|
57302
|
+
type: external_exports.enum(["orgfs", "orgdocs", "envdb"]),
|
|
57303
|
+
id: external_exports.string(),
|
|
57304
|
+
action: external_exports.enum(["read", "write", "admin"]),
|
|
57305
|
+
scope_required: external_exports.boolean(),
|
|
57306
|
+
scope_matched: external_exports.boolean()
|
|
57307
|
+
}).optional()
|
|
56984
57308
|
});
|
|
56985
57309
|
var AccessExplainGrantSchema = external_exports.object({
|
|
56986
57310
|
source: external_exports.string(),
|
|
56987
57311
|
role: external_exports.string().optional(),
|
|
56988
57312
|
permissions: external_exports.array(external_exports.string()),
|
|
56989
|
-
has_permission: external_exports.boolean()
|
|
57313
|
+
has_permission: external_exports.boolean(),
|
|
57314
|
+
scope_json: AccessBindingScopeSchema.nullable().optional(),
|
|
57315
|
+
scope_match: external_exports.boolean().optional(),
|
|
57316
|
+
scope_reason: external_exports.string().optional()
|
|
56990
57317
|
});
|
|
56991
57318
|
var AccessExplainResponseSchema = external_exports.object({
|
|
56992
57319
|
permission: external_exports.string(),
|
|
56993
57320
|
result: external_exports.enum(["ALLOWED", "DENIED"]),
|
|
56994
57321
|
grants: external_exports.array(AccessExplainGrantSchema),
|
|
56995
|
-
missing_reason: external_exports.string().optional()
|
|
57322
|
+
missing_reason: external_exports.string().optional(),
|
|
57323
|
+
resource: external_exports.object({
|
|
57324
|
+
type: external_exports.enum(["orgfs", "orgdocs", "envdb"]),
|
|
57325
|
+
id: external_exports.string(),
|
|
57326
|
+
action: external_exports.enum(["read", "write", "admin"]),
|
|
57327
|
+
scope_required: external_exports.boolean(),
|
|
57328
|
+
scope_matched: external_exports.boolean()
|
|
57329
|
+
}).optional()
|
|
57330
|
+
});
|
|
57331
|
+
var AccessMembershipBaseSchema = external_exports.object({
|
|
57332
|
+
org_role: external_exports.enum(["owner", "admin", "member"]).nullable(),
|
|
57333
|
+
project_roles: external_exports.array(external_exports.object({
|
|
57334
|
+
project_id: external_exports.string(),
|
|
57335
|
+
role: external_exports.enum(["owner", "admin", "member"])
|
|
57336
|
+
})),
|
|
57337
|
+
token_scopes: external_exports.array(external_exports.string())
|
|
57338
|
+
});
|
|
57339
|
+
var AccessGroupSummarySchema = external_exports.object({
|
|
57340
|
+
id: external_exports.string(),
|
|
57341
|
+
slug: external_exports.string(),
|
|
57342
|
+
name: external_exports.string()
|
|
57343
|
+
});
|
|
57344
|
+
var AccessResolvedBindingSchema = AccessBindingResponseSchema.extend({
|
|
57345
|
+
role_permissions: external_exports.array(external_exports.string()),
|
|
57346
|
+
matched_via: external_exports.enum(["direct", "group"]).optional(),
|
|
57347
|
+
matched_group_id: external_exports.string().nullable().optional(),
|
|
57348
|
+
matched_group_slug: external_exports.string().nullable().optional()
|
|
57349
|
+
});
|
|
57350
|
+
var AccessEffectiveScopeSummarySchema = external_exports.object({
|
|
57351
|
+
orgfs: external_exports.object({
|
|
57352
|
+
allow_prefixes: external_exports.array(external_exports.string()),
|
|
57353
|
+
read_only_prefixes: external_exports.array(external_exports.string())
|
|
57354
|
+
}),
|
|
57355
|
+
orgdocs: external_exports.object({
|
|
57356
|
+
allow_prefixes: external_exports.array(external_exports.string()),
|
|
57357
|
+
read_only_prefixes: external_exports.array(external_exports.string())
|
|
57358
|
+
}),
|
|
57359
|
+
envdb: external_exports.object({
|
|
57360
|
+
schemas: external_exports.array(external_exports.string()),
|
|
57361
|
+
tables: external_exports.array(external_exports.string())
|
|
57362
|
+
})
|
|
57363
|
+
});
|
|
57364
|
+
var AccessPrincipalMembershipsResponseSchema = external_exports.object({
|
|
57365
|
+
org_id: external_exports.string(),
|
|
57366
|
+
principal_type: AccessPrincipalTypeSchema,
|
|
57367
|
+
principal_id: external_exports.string(),
|
|
57368
|
+
base: AccessMembershipBaseSchema,
|
|
57369
|
+
groups: external_exports.array(AccessGroupSummarySchema),
|
|
57370
|
+
direct_bindings: external_exports.array(AccessBindingResponseSchema),
|
|
57371
|
+
effective_bindings: external_exports.array(AccessResolvedBindingSchema),
|
|
57372
|
+
effective_permissions: external_exports.array(external_exports.string()),
|
|
57373
|
+
effective_scopes: AccessEffectiveScopeSummarySchema
|
|
56996
57374
|
});
|
|
56997
57375
|
var AccessYamlRoleSchema = external_exports.object({
|
|
56998
57376
|
scope: external_exports.enum(["org", "project"]),
|
|
56999
57377
|
description: external_exports.string().optional(),
|
|
57000
57378
|
permissions: external_exports.array(external_exports.string()).min(1)
|
|
57001
57379
|
});
|
|
57380
|
+
var AccessYamlGroupMemberSchema = external_exports.object({
|
|
57381
|
+
type: AccessGroupMemberPrincipalTypeSchema,
|
|
57382
|
+
id: external_exports.string().min(1)
|
|
57383
|
+
});
|
|
57384
|
+
var AccessYamlGroupSchema = external_exports.object({
|
|
57385
|
+
name: external_exports.string().min(1).max(100).optional(),
|
|
57386
|
+
description: external_exports.string().max(500).optional(),
|
|
57387
|
+
members: external_exports.array(AccessYamlGroupMemberSchema).optional()
|
|
57388
|
+
});
|
|
57002
57389
|
var AccessYamlBindingSchema = external_exports.object({
|
|
57003
|
-
scope: external_exports.enum(["org", "project"]),
|
|
57004
57390
|
project_id: external_exports.string().optional(),
|
|
57005
57391
|
subject: external_exports.object({
|
|
57006
|
-
type:
|
|
57392
|
+
type: AccessPrincipalTypeSchema,
|
|
57007
57393
|
id: external_exports.string()
|
|
57008
57394
|
}),
|
|
57009
|
-
roles: external_exports.array(external_exports.string()).min(1)
|
|
57395
|
+
roles: external_exports.array(external_exports.string()).min(1),
|
|
57396
|
+
scope: AccessBindingScopeSchema.optional()
|
|
57010
57397
|
});
|
|
57011
57398
|
var AccessYamlSchema = external_exports.object({
|
|
57012
|
-
version: external_exports.literal(
|
|
57399
|
+
version: external_exports.literal(2),
|
|
57013
57400
|
access: external_exports.object({
|
|
57401
|
+
groups: external_exports.record(external_exports.string().regex(/^[a-z0-9][a-z0-9-_]*$/), AccessYamlGroupSchema).optional(),
|
|
57014
57402
|
roles: external_exports.record(external_exports.string(), AccessYamlRoleSchema).optional(),
|
|
57015
57403
|
bindings: external_exports.array(AccessYamlBindingSchema).optional()
|
|
57016
57404
|
})
|
|
@@ -57561,8 +57949,21 @@ var DbRlsTableSchema = external_exports.object({
|
|
|
57561
57949
|
rls_enabled: external_exports.boolean(),
|
|
57562
57950
|
policies: external_exports.array(DbPolicySchema)
|
|
57563
57951
|
});
|
|
57952
|
+
var DbRlsDiagnosticsContextSchema = external_exports.object({
|
|
57953
|
+
user_id: external_exports.string().nullable(),
|
|
57954
|
+
principal_type: external_exports.enum(["user", "service_principal"]).nullable(),
|
|
57955
|
+
org_id: external_exports.string().nullable(),
|
|
57956
|
+
project_id: external_exports.string().nullable(),
|
|
57957
|
+
env_name: external_exports.string().nullable(),
|
|
57958
|
+
group_ids: external_exports.array(external_exports.string()),
|
|
57959
|
+
permissions: external_exports.array(external_exports.string())
|
|
57960
|
+
});
|
|
57961
|
+
var DbRlsDiagnosticsSchema = external_exports.object({
|
|
57962
|
+
context: DbRlsDiagnosticsContextSchema
|
|
57963
|
+
});
|
|
57564
57964
|
var DbRlsResponseSchema = external_exports.object({
|
|
57565
|
-
tables: external_exports.array(DbRlsTableSchema)
|
|
57965
|
+
tables: external_exports.array(DbRlsTableSchema),
|
|
57966
|
+
diagnostics: DbRlsDiagnosticsSchema
|
|
57566
57967
|
});
|
|
57567
57968
|
var DbSqlRequestSchema = external_exports.object({
|
|
57568
57969
|
sql: external_exports.string().min(1),
|
|
@@ -58363,6 +58764,186 @@ var OrgDocumentQueryResponseSchema = external_exports.object({
|
|
|
58363
58764
|
})
|
|
58364
58765
|
});
|
|
58365
58766
|
|
|
58767
|
+
// ../shared/dist/schemas/org-fs-sync.js
|
|
58768
|
+
var OrgFsSyncModeSchema = external_exports.enum(["two_way", "push_only", "pull_only"]);
|
|
58769
|
+
var OrgFsLinkStatusSchema = external_exports.enum(["active", "paused", "revoked"]);
|
|
58770
|
+
var OrgFsDeviceStatusSchema = external_exports.enum(["active", "revoked"]);
|
|
58771
|
+
var OrgFsEventSourceSideSchema = external_exports.enum(["local", "remote", "system"]);
|
|
58772
|
+
var OrgFsOwnerPrincipalTypeSchema = external_exports.enum(["user", "service_principal", "system"]);
|
|
58773
|
+
var OrgFsLinkScopeSchema = external_exports.object({
|
|
58774
|
+
allow_prefixes: external_exports.array(external_exports.string().min(1)),
|
|
58775
|
+
read_only_prefixes: external_exports.array(external_exports.string().min(1)).optional()
|
|
58776
|
+
}).passthrough();
|
|
58777
|
+
var OrgFsDeviceSchema = external_exports.object({
|
|
58778
|
+
id: external_exports.string(),
|
|
58779
|
+
org_id: external_exports.string(),
|
|
58780
|
+
device_name: external_exports.string(),
|
|
58781
|
+
platform: external_exports.string().nullable(),
|
|
58782
|
+
client_version: external_exports.string().nullable(),
|
|
58783
|
+
public_key: external_exports.string(),
|
|
58784
|
+
status: OrgFsDeviceStatusSchema,
|
|
58785
|
+
last_seen_at: external_exports.string().nullable(),
|
|
58786
|
+
created_at: external_exports.string(),
|
|
58787
|
+
updated_at: external_exports.string()
|
|
58788
|
+
});
|
|
58789
|
+
var OrgFsEnrollmentSchema = external_exports.object({
|
|
58790
|
+
token: external_exports.string(),
|
|
58791
|
+
expires_at: external_exports.string(),
|
|
58792
|
+
gateway_url: external_exports.string().url()
|
|
58793
|
+
});
|
|
58794
|
+
var OrgFsEnrollDeviceRequestSchema = external_exports.object({
|
|
58795
|
+
device_name: external_exports.string().min(1),
|
|
58796
|
+
platform: external_exports.string().optional(),
|
|
58797
|
+
client_version: external_exports.string().optional(),
|
|
58798
|
+
public_key: external_exports.string().optional()
|
|
58799
|
+
});
|
|
58800
|
+
var OrgFsEnrollDeviceResponseSchema = external_exports.object({
|
|
58801
|
+
device: OrgFsDeviceSchema,
|
|
58802
|
+
enrollment: OrgFsEnrollmentSchema
|
|
58803
|
+
});
|
|
58804
|
+
var OrgFsLinkSchema = external_exports.object({
|
|
58805
|
+
id: external_exports.string(),
|
|
58806
|
+
org_id: external_exports.string(),
|
|
58807
|
+
device_id: external_exports.string(),
|
|
58808
|
+
owner_principal_type: OrgFsOwnerPrincipalTypeSchema,
|
|
58809
|
+
owner_principal_id: external_exports.string().nullable(),
|
|
58810
|
+
mode: OrgFsSyncModeSchema,
|
|
58811
|
+
status: OrgFsLinkStatusSchema,
|
|
58812
|
+
local_path: external_exports.string(),
|
|
58813
|
+
remote_path: external_exports.string(),
|
|
58814
|
+
scope_json: OrgFsLinkScopeSchema,
|
|
58815
|
+
includes: external_exports.array(external_exports.string()),
|
|
58816
|
+
excludes: external_exports.array(external_exports.string()),
|
|
58817
|
+
last_cursor: external_exports.number().int().nonnegative(),
|
|
58818
|
+
lag_ms: external_exports.number().int().nonnegative().nullable().optional(),
|
|
58819
|
+
backlog: external_exports.number().int().nonnegative().optional(),
|
|
58820
|
+
last_synced_at: external_exports.string().nullable(),
|
|
58821
|
+
last_heartbeat_at: external_exports.string().nullable(),
|
|
58822
|
+
updated_at: external_exports.string(),
|
|
58823
|
+
created_at: external_exports.string()
|
|
58824
|
+
});
|
|
58825
|
+
var OrgFsCreateLinkRequestSchema = external_exports.object({
|
|
58826
|
+
device_id: external_exports.string().min(1),
|
|
58827
|
+
mode: OrgFsSyncModeSchema.default("two_way"),
|
|
58828
|
+
local_path: external_exports.string().min(1),
|
|
58829
|
+
remote_path: external_exports.string().min(1).default("/"),
|
|
58830
|
+
allow_prefixes: external_exports.array(external_exports.string().min(1)).min(1).optional(),
|
|
58831
|
+
includes: external_exports.array(external_exports.string()).optional(),
|
|
58832
|
+
excludes: external_exports.array(external_exports.string()).optional()
|
|
58833
|
+
});
|
|
58834
|
+
var OrgFsLinkGatewayTokenSchema = external_exports.object({
|
|
58835
|
+
token: external_exports.string(),
|
|
58836
|
+
expires_at: external_exports.string(),
|
|
58837
|
+
header: external_exports.string().default("x-eve-internal-token"),
|
|
58838
|
+
link_id: external_exports.string(),
|
|
58839
|
+
mode: OrgFsSyncModeSchema,
|
|
58840
|
+
allow_prefixes: external_exports.array(external_exports.string())
|
|
58841
|
+
});
|
|
58842
|
+
var OrgFsCreateLinkResponseSchema = external_exports.object({
|
|
58843
|
+
link: OrgFsLinkSchema,
|
|
58844
|
+
runtime: external_exports.object({
|
|
58845
|
+
sync_engine: external_exports.literal("syncthing"),
|
|
58846
|
+
profile: external_exports.string(),
|
|
58847
|
+
gateway: OrgFsLinkGatewayTokenSchema
|
|
58848
|
+
})
|
|
58849
|
+
});
|
|
58850
|
+
var OrgFsRotateLinkTokenResponseSchema = external_exports.object({
|
|
58851
|
+
gateway: OrgFsLinkGatewayTokenSchema
|
|
58852
|
+
});
|
|
58853
|
+
var OrgFsListLinksResponseSchema = external_exports.object({
|
|
58854
|
+
data: external_exports.array(OrgFsLinkSchema)
|
|
58855
|
+
});
|
|
58856
|
+
var OrgFsUpdateLinkRequestSchema = external_exports.object({
|
|
58857
|
+
mode: OrgFsSyncModeSchema.optional(),
|
|
58858
|
+
status: OrgFsLinkStatusSchema.optional(),
|
|
58859
|
+
allow_prefixes: external_exports.array(external_exports.string().min(1)).min(1).optional(),
|
|
58860
|
+
includes: external_exports.array(external_exports.string()).optional(),
|
|
58861
|
+
excludes: external_exports.array(external_exports.string()).optional()
|
|
58862
|
+
}).refine((value) => Object.keys(value).length > 0, {
|
|
58863
|
+
message: "At least one field is required"
|
|
58864
|
+
});
|
|
58865
|
+
var OrgFsDeleteLinkResponseSchema = external_exports.object({
|
|
58866
|
+
success: external_exports.boolean()
|
|
58867
|
+
});
|
|
58868
|
+
var OrgFsStatusResponseSchema = external_exports.object({
|
|
58869
|
+
org_id: external_exports.string(),
|
|
58870
|
+
gateway: external_exports.object({
|
|
58871
|
+
status: external_exports.enum(["healthy", "degraded", "offline"]),
|
|
58872
|
+
last_heartbeat_at: external_exports.string().nullable()
|
|
58873
|
+
}),
|
|
58874
|
+
links: external_exports.object({
|
|
58875
|
+
active: external_exports.number().int().nonnegative(),
|
|
58876
|
+
paused: external_exports.number().int().nonnegative(),
|
|
58877
|
+
revoked: external_exports.number().int().nonnegative()
|
|
58878
|
+
}),
|
|
58879
|
+
events: external_exports.object({
|
|
58880
|
+
latest_seq: external_exports.number().int().nonnegative()
|
|
58881
|
+
})
|
|
58882
|
+
});
|
|
58883
|
+
var OrgFsEventSchema = external_exports.object({
|
|
58884
|
+
seq: external_exports.number().int().nonnegative(),
|
|
58885
|
+
event_id: external_exports.string(),
|
|
58886
|
+
org_id: external_exports.string(),
|
|
58887
|
+
link_id: external_exports.string().nullable().optional(),
|
|
58888
|
+
device_id: external_exports.string().nullable().optional(),
|
|
58889
|
+
event_type: external_exports.string(),
|
|
58890
|
+
path: external_exports.string(),
|
|
58891
|
+
content_hash: external_exports.string().nullable().optional(),
|
|
58892
|
+
size_bytes: external_exports.number().int().nonnegative().nullable().optional(),
|
|
58893
|
+
source_side: OrgFsEventSourceSideSchema,
|
|
58894
|
+
metadata: external_exports.record(external_exports.unknown()).optional(),
|
|
58895
|
+
created_at: external_exports.string()
|
|
58896
|
+
});
|
|
58897
|
+
var OrgFsEventListResponseSchema = external_exports.object({
|
|
58898
|
+
data: external_exports.array(OrgFsEventSchema),
|
|
58899
|
+
pagination: external_exports.object({
|
|
58900
|
+
limit: external_exports.number().int().positive(),
|
|
58901
|
+
next_after_seq: external_exports.number().int().nonnegative().nullable()
|
|
58902
|
+
})
|
|
58903
|
+
});
|
|
58904
|
+
var OrgFsConflictSchema = external_exports.object({
|
|
58905
|
+
id: external_exports.string(),
|
|
58906
|
+
org_id: external_exports.string(),
|
|
58907
|
+
link_id: external_exports.string().nullable(),
|
|
58908
|
+
path: external_exports.string(),
|
|
58909
|
+
local_hash: external_exports.string().nullable(),
|
|
58910
|
+
remote_hash: external_exports.string().nullable(),
|
|
58911
|
+
status: external_exports.enum(["open", "resolved"]),
|
|
58912
|
+
resolution: external_exports.enum(["pick_local", "pick_remote", "manual"]).nullable(),
|
|
58913
|
+
resolved_by: external_exports.string().nullable(),
|
|
58914
|
+
resolved_at: external_exports.string().nullable(),
|
|
58915
|
+
created_at: external_exports.string()
|
|
58916
|
+
});
|
|
58917
|
+
var OrgFsListConflictsResponseSchema = external_exports.object({
|
|
58918
|
+
data: external_exports.array(OrgFsConflictSchema)
|
|
58919
|
+
});
|
|
58920
|
+
var OrgFsResolveConflictRequestSchema = external_exports.object({
|
|
58921
|
+
strategy: external_exports.enum(["pick_local", "pick_remote", "manual"]),
|
|
58922
|
+
merged_content: external_exports.string().optional()
|
|
58923
|
+
});
|
|
58924
|
+
var OrgFsResolveConflictResponseSchema = external_exports.object({
|
|
58925
|
+
conflict: OrgFsConflictSchema
|
|
58926
|
+
});
|
|
58927
|
+
var OrgFsInternalIngestEventRequestSchema = external_exports.object({
|
|
58928
|
+
event_id: external_exports.string(),
|
|
58929
|
+
link_id: external_exports.string().optional(),
|
|
58930
|
+
device_id: external_exports.string().optional(),
|
|
58931
|
+
event_type: external_exports.string(),
|
|
58932
|
+
path: external_exports.string(),
|
|
58933
|
+
content_hash: external_exports.string().optional(),
|
|
58934
|
+
size_bytes: external_exports.number().int().nonnegative().optional(),
|
|
58935
|
+
source_side: OrgFsEventSourceSideSchema,
|
|
58936
|
+
metadata: external_exports.record(external_exports.unknown()).optional()
|
|
58937
|
+
});
|
|
58938
|
+
var OrgFsInternalHeartbeatRequestSchema = external_exports.object({
|
|
58939
|
+
cursor: external_exports.number().int().nonnegative().optional(),
|
|
58940
|
+
backlog: external_exports.number().int().nonnegative().optional(),
|
|
58941
|
+
lag_ms: external_exports.number().int().nonnegative().optional()
|
|
58942
|
+
});
|
|
58943
|
+
var OrgFsInternalMetricsRequestSchema = external_exports.object({
|
|
58944
|
+
metrics: external_exports.record(external_exports.unknown())
|
|
58945
|
+
});
|
|
58946
|
+
|
|
58366
58947
|
// ../shared/dist/schemas/resource.js
|
|
58367
58948
|
var ResolveResourcesRequestSchema = external_exports.object({
|
|
58368
58949
|
uris: external_exports.array(external_exports.string().min(1)),
|
|
@@ -60767,6 +61348,14 @@ var ALL_PERMISSIONS = [
|
|
|
60767
61348
|
// Env DB
|
|
60768
61349
|
"envdb:read",
|
|
60769
61350
|
"envdb:write",
|
|
61351
|
+
// Org filesystem data plane
|
|
61352
|
+
"orgfs:read",
|
|
61353
|
+
"orgfs:write",
|
|
61354
|
+
"orgfs:admin",
|
|
61355
|
+
// Org document data plane
|
|
61356
|
+
"orgdocs:read",
|
|
61357
|
+
"orgdocs:write",
|
|
61358
|
+
"orgdocs:admin",
|
|
60770
61359
|
// Secrets
|
|
60771
61360
|
"secrets:read",
|
|
60772
61361
|
"secrets:write",
|
|
@@ -61688,11 +62277,11 @@ function formatJobsTable(jobs) {
|
|
|
61688
62277
|
const phaseWidth = Math.max(5, ...jobs.map((j) => j.phase.length));
|
|
61689
62278
|
const titleWidth = Math.min(50, Math.max(5, ...jobs.map((j) => j.title.length)));
|
|
61690
62279
|
const header = [
|
|
61691
|
-
|
|
61692
|
-
|
|
61693
|
-
|
|
61694
|
-
|
|
61695
|
-
|
|
62280
|
+
padRight2("ID", idWidth),
|
|
62281
|
+
padRight2("P", 2),
|
|
62282
|
+
padRight2("Phase", phaseWidth),
|
|
62283
|
+
padRight2("Type", 8),
|
|
62284
|
+
padRight2("Title", titleWidth),
|
|
61696
62285
|
"Assignee"
|
|
61697
62286
|
].join(" ");
|
|
61698
62287
|
console.log(header);
|
|
@@ -61700,11 +62289,11 @@ function formatJobsTable(jobs) {
|
|
|
61700
62289
|
for (const job of jobs) {
|
|
61701
62290
|
const title = job.title.length > titleWidth ? job.title.slice(0, titleWidth - 3) + "..." : job.title;
|
|
61702
62291
|
const row = [
|
|
61703
|
-
|
|
61704
|
-
|
|
61705
|
-
|
|
61706
|
-
|
|
61707
|
-
|
|
62292
|
+
padRight2(job.id, idWidth),
|
|
62293
|
+
padRight2(`P${job.priority}`, 2),
|
|
62294
|
+
padRight2(job.phase, phaseWidth),
|
|
62295
|
+
padRight2(job.issue_type, 8),
|
|
62296
|
+
padRight2(title, titleWidth),
|
|
61708
62297
|
job.assignee ?? "-"
|
|
61709
62298
|
].join(" ");
|
|
61710
62299
|
console.log(row);
|
|
@@ -63344,7 +63933,7 @@ function formatDuration(startStr, endStr) {
|
|
|
63344
63933
|
return "unknown";
|
|
63345
63934
|
}
|
|
63346
63935
|
}
|
|
63347
|
-
function
|
|
63936
|
+
function padRight2(str, width) {
|
|
63348
63937
|
if (str.length >= width) return str;
|
|
63349
63938
|
return str + " ".repeat(width - str.length);
|
|
63350
63939
|
}
|
|
@@ -63938,11 +64527,11 @@ Permissions (${data.permissions.length}):`);
|
|
|
63938
64527
|
}
|
|
63939
64528
|
if (status) {
|
|
63940
64529
|
if (status.completed) {
|
|
63941
|
-
|
|
64530
|
+
console.log("Bootstrap already completed. Attempting server-side recovery flow.");
|
|
63942
64531
|
}
|
|
63943
|
-
if (!status.requires_token && status.window_open) {
|
|
64532
|
+
if (!status.completed && !status.requires_token && status.window_open) {
|
|
63944
64533
|
console.log(`Bootstrap window open (${status.mode} mode). Token not required.`);
|
|
63945
|
-
} else if (status.requires_token && !token) {
|
|
64534
|
+
} else if (!status.completed && status.requires_token && !token) {
|
|
63946
64535
|
throw new Error("Bootstrap token required. Use --token <token> or set EVE_BOOTSTRAP_TOKEN");
|
|
63947
64536
|
}
|
|
63948
64537
|
} else if (!token) {
|
|
@@ -64977,10 +65566,10 @@ function renderDiscoveredModels(response) {
|
|
|
64977
65566
|
${response.models.length} model(s) discovered`);
|
|
64978
65567
|
}
|
|
64979
65568
|
function formatRow2(columns, widths) {
|
|
64980
|
-
const cells = columns.map((value, idx) => ` ${
|
|
65569
|
+
const cells = columns.map((value, idx) => ` ${padRight3(value, widths[idx])} `);
|
|
64981
65570
|
return `|${cells.join("|")}|`;
|
|
64982
65571
|
}
|
|
64983
|
-
function
|
|
65572
|
+
function padRight3(value, width) {
|
|
64984
65573
|
return value.length >= width ? value : `${value}${" ".repeat(width - value.length)}`;
|
|
64985
65574
|
}
|
|
64986
65575
|
|
|
@@ -65195,7 +65784,7 @@ async function handleSystem(subcommand, positionals, flags, context2) {
|
|
|
65195
65784
|
const json = Boolean(flags.json);
|
|
65196
65785
|
switch (subcommand) {
|
|
65197
65786
|
case "status":
|
|
65198
|
-
return
|
|
65787
|
+
return handleStatus2(context2, json);
|
|
65199
65788
|
case "health":
|
|
65200
65789
|
return handleHealth(context2, json);
|
|
65201
65790
|
case "jobs":
|
|
@@ -65218,7 +65807,7 @@ async function handleSystem(subcommand, positionals, flags, context2) {
|
|
|
65218
65807
|
throw new Error("Usage: eve system <status|health|jobs|envs|logs|pods|events|config|settings|orchestrator>");
|
|
65219
65808
|
}
|
|
65220
65809
|
}
|
|
65221
|
-
async function
|
|
65810
|
+
async function handleStatus2(context2, json) {
|
|
65222
65811
|
try {
|
|
65223
65812
|
const status = await requestJson(context2, "/system/status");
|
|
65224
65813
|
if (json) {
|
|
@@ -65584,11 +66173,11 @@ function formatJobsTable2(jobs) {
|
|
|
65584
66173
|
const phaseWidth = Math.max(5, ...jobs.map((j) => j.phase.length));
|
|
65585
66174
|
const titleWidth = Math.min(40, Math.max(5, ...jobs.map((j) => j.title.length)));
|
|
65586
66175
|
const header = [
|
|
65587
|
-
|
|
65588
|
-
|
|
65589
|
-
|
|
65590
|
-
|
|
65591
|
-
|
|
66176
|
+
padRight4("Job ID", idWidth),
|
|
66177
|
+
padRight4("Project", projectWidth),
|
|
66178
|
+
padRight4("Phase", phaseWidth),
|
|
66179
|
+
padRight4("P", 2),
|
|
66180
|
+
padRight4("Title", titleWidth),
|
|
65592
66181
|
"Assignee"
|
|
65593
66182
|
].join(" ");
|
|
65594
66183
|
console.log(header);
|
|
@@ -65596,11 +66185,11 @@ function formatJobsTable2(jobs) {
|
|
|
65596
66185
|
for (const job of jobs) {
|
|
65597
66186
|
const title = job.title.length > titleWidth ? job.title.slice(0, titleWidth - 3) + "..." : job.title;
|
|
65598
66187
|
const row = [
|
|
65599
|
-
|
|
65600
|
-
|
|
65601
|
-
|
|
65602
|
-
|
|
65603
|
-
|
|
66188
|
+
padRight4(job.id, idWidth),
|
|
66189
|
+
padRight4(job.project_id, projectWidth),
|
|
66190
|
+
padRight4(job.phase, phaseWidth),
|
|
66191
|
+
padRight4(`P${job.priority}`, 2),
|
|
66192
|
+
padRight4(title, titleWidth),
|
|
65604
66193
|
job.assignee ?? "-"
|
|
65605
66194
|
].join(" ");
|
|
65606
66195
|
console.log(row);
|
|
@@ -65619,21 +66208,21 @@ function formatEnvsTable(envs) {
|
|
|
65619
66208
|
const namespaceWidth = Math.max(9, ...envs.map((e) => (e.namespace ?? "-").length));
|
|
65620
66209
|
const releaseWidth = Math.max(7, ...envs.map((e) => (e.current_release ?? "-").length));
|
|
65621
66210
|
const header = [
|
|
65622
|
-
|
|
65623
|
-
|
|
65624
|
-
|
|
65625
|
-
|
|
65626
|
-
|
|
66211
|
+
padRight4("Project", projectWidth),
|
|
66212
|
+
padRight4("Environment", nameWidth),
|
|
66213
|
+
padRight4("Type", typeWidth),
|
|
66214
|
+
padRight4("Namespace", namespaceWidth),
|
|
66215
|
+
padRight4("Release", releaseWidth)
|
|
65627
66216
|
].join(" ");
|
|
65628
66217
|
console.log(header);
|
|
65629
66218
|
console.log("-".repeat(header.length));
|
|
65630
66219
|
for (const env of envs) {
|
|
65631
66220
|
const row = [
|
|
65632
|
-
|
|
65633
|
-
|
|
65634
|
-
|
|
65635
|
-
|
|
65636
|
-
|
|
66221
|
+
padRight4(env.project_id, projectWidth),
|
|
66222
|
+
padRight4(env.name, nameWidth),
|
|
66223
|
+
padRight4(env.type, typeWidth),
|
|
66224
|
+
padRight4(env.namespace ?? "-", namespaceWidth),
|
|
66225
|
+
padRight4(env.current_release ?? "-", releaseWidth)
|
|
65637
66226
|
].join(" ");
|
|
65638
66227
|
console.log(row);
|
|
65639
66228
|
}
|
|
@@ -65652,12 +66241,12 @@ function formatPodsTable(pods) {
|
|
|
65652
66241
|
const restartsWidth = Math.max(8, ...pods.map((p) => String(p.restarts).length));
|
|
65653
66242
|
const ageWidth = Math.max(3, ...pods.map((p) => p.age.length));
|
|
65654
66243
|
const header = [
|
|
65655
|
-
|
|
65656
|
-
|
|
65657
|
-
|
|
65658
|
-
|
|
65659
|
-
|
|
65660
|
-
|
|
66244
|
+
padRight4("Name", nameWidth),
|
|
66245
|
+
padRight4("Namespace", nsWidth),
|
|
66246
|
+
padRight4("Phase", phaseWidth),
|
|
66247
|
+
padRight4("Ready", readyWidth),
|
|
66248
|
+
padRight4("Restarts", restartsWidth),
|
|
66249
|
+
padRight4("Age", ageWidth),
|
|
65661
66250
|
"Component",
|
|
65662
66251
|
"Org",
|
|
65663
66252
|
"Project",
|
|
@@ -65667,20 +66256,20 @@ function formatPodsTable(pods) {
|
|
|
65667
66256
|
console.log("-".repeat(header.length));
|
|
65668
66257
|
for (const pod of pods) {
|
|
65669
66258
|
console.log([
|
|
65670
|
-
|
|
65671
|
-
|
|
65672
|
-
|
|
65673
|
-
|
|
65674
|
-
|
|
65675
|
-
|
|
65676
|
-
|
|
65677
|
-
|
|
65678
|
-
|
|
65679
|
-
|
|
66259
|
+
padRight4(pod.name, nameWidth),
|
|
66260
|
+
padRight4(pod.namespace, nsWidth),
|
|
66261
|
+
padRight4(pod.phase, phaseWidth),
|
|
66262
|
+
padRight4(pod.ready ? "yes" : "no", readyWidth),
|
|
66263
|
+
padRight4(String(pod.restarts), restartsWidth),
|
|
66264
|
+
padRight4(pod.age, ageWidth),
|
|
66265
|
+
padRight4(pod.component ?? "-", 10),
|
|
66266
|
+
padRight4(pod.orgId ?? "-", 10),
|
|
66267
|
+
padRight4(pod.projectId ?? "-", 10),
|
|
66268
|
+
padRight4(pod.env ?? "-", 10)
|
|
65680
66269
|
].join(" "));
|
|
65681
66270
|
}
|
|
65682
66271
|
}
|
|
65683
|
-
function
|
|
66272
|
+
function padRight4(str, width) {
|
|
65684
66273
|
if (str.length >= width) return str;
|
|
65685
66274
|
return str + " ".repeat(width - str.length);
|
|
65686
66275
|
}
|
|
@@ -65897,6 +66486,8 @@ function stripFilePath(filePath) {
|
|
|
65897
66486
|
}
|
|
65898
66487
|
|
|
65899
66488
|
// src/commands/env.ts
|
|
66489
|
+
var import_node_fs7 = require("node:fs");
|
|
66490
|
+
var import_node_path7 = require("node:path");
|
|
65900
66491
|
var readline2 = __toESM(require("node:readline/promises"));
|
|
65901
66492
|
async function handleEnv(subcommand, positionals, flags, context2) {
|
|
65902
66493
|
const json = Boolean(flags.json);
|
|
@@ -66048,12 +66639,47 @@ async function handleDeploy(positionals, flags, context2, json) {
|
|
|
66048
66639
|
if (!json) {
|
|
66049
66640
|
console.log(`Deploying commit ${gitSha.substring(0, 8)} to ${envName}...`);
|
|
66050
66641
|
}
|
|
66051
|
-
|
|
66052
|
-
|
|
66053
|
-
|
|
66054
|
-
|
|
66055
|
-
|
|
66056
|
-
|
|
66642
|
+
let manifestHash;
|
|
66643
|
+
if (repoDir) {
|
|
66644
|
+
const manifestPath = (0, import_node_path7.join)(repoDir, ".eve", "manifest.yaml");
|
|
66645
|
+
if ((0, import_node_fs7.existsSync)(manifestPath)) {
|
|
66646
|
+
const manifestYaml = (0, import_node_fs7.readFileSync)(manifestPath, "utf-8");
|
|
66647
|
+
const branch = getGitBranch(repoDir);
|
|
66648
|
+
const syncResponse = await requestJson(
|
|
66649
|
+
context2,
|
|
66650
|
+
`/projects/${projectId}/manifest`,
|
|
66651
|
+
{
|
|
66652
|
+
method: "POST",
|
|
66653
|
+
body: {
|
|
66654
|
+
yaml: manifestYaml,
|
|
66655
|
+
git_sha: gitSha,
|
|
66656
|
+
branch
|
|
66657
|
+
}
|
|
66658
|
+
}
|
|
66659
|
+
);
|
|
66660
|
+
manifestHash = syncResponse.manifest_hash;
|
|
66661
|
+
if (!json) {
|
|
66662
|
+
console.log(`Synced manifest ${manifestHash.substring(0, 8)}...`);
|
|
66663
|
+
}
|
|
66664
|
+
} else {
|
|
66665
|
+
const manifest = await requestJson(
|
|
66666
|
+
context2,
|
|
66667
|
+
`/projects/${projectId}/manifest`
|
|
66668
|
+
);
|
|
66669
|
+
manifestHash = manifest.manifest_hash;
|
|
66670
|
+
if (!json) {
|
|
66671
|
+
console.log(`Using manifest ${manifestHash.substring(0, 8)}...`);
|
|
66672
|
+
}
|
|
66673
|
+
}
|
|
66674
|
+
} else {
|
|
66675
|
+
const manifest = await requestJson(
|
|
66676
|
+
context2,
|
|
66677
|
+
`/projects/${projectId}/manifest`
|
|
66678
|
+
);
|
|
66679
|
+
manifestHash = manifest.manifest_hash;
|
|
66680
|
+
if (!json) {
|
|
66681
|
+
console.log(`Using manifest ${manifestHash.substring(0, 8)}...`);
|
|
66682
|
+
}
|
|
66057
66683
|
}
|
|
66058
66684
|
const direct = Boolean(flags.direct);
|
|
66059
66685
|
let inputs;
|
|
@@ -66074,7 +66700,7 @@ Example: --inputs '{"release_id":"rel_xxx","smoke_test":false}'`
|
|
|
66074
66700
|
const imageTag = getStringFlag(flags, ["image-tag", "image_tag", "imageTag"]);
|
|
66075
66701
|
const body = {
|
|
66076
66702
|
git_sha: gitSha,
|
|
66077
|
-
manifest_hash:
|
|
66703
|
+
manifest_hash: manifestHash
|
|
66078
66704
|
};
|
|
66079
66705
|
if (direct) {
|
|
66080
66706
|
body.direct = true;
|
|
@@ -66351,9 +66977,9 @@ function formatEnvironmentsTable(environments) {
|
|
|
66351
66977
|
const typeWidth = Math.max(4, ...environments.map((e) => e.type.length));
|
|
66352
66978
|
const namespaceWidth = Math.max(9, ...environments.map((e) => e.namespace?.length ?? 0));
|
|
66353
66979
|
const header = [
|
|
66354
|
-
|
|
66355
|
-
|
|
66356
|
-
|
|
66980
|
+
padRight5("Name", nameWidth),
|
|
66981
|
+
padRight5("Type", typeWidth),
|
|
66982
|
+
padRight5("Namespace", namespaceWidth),
|
|
66357
66983
|
"Current Release"
|
|
66358
66984
|
].join(" ");
|
|
66359
66985
|
console.log(header);
|
|
@@ -66361,9 +66987,9 @@ function formatEnvironmentsTable(environments) {
|
|
|
66361
66987
|
for (const env of environments) {
|
|
66362
66988
|
const releaseDisplay = env.current_release_id ? env.current_release_id.substring(0, 12) + "..." : "-";
|
|
66363
66989
|
const row = [
|
|
66364
|
-
|
|
66365
|
-
|
|
66366
|
-
|
|
66990
|
+
padRight5(env.name, nameWidth),
|
|
66991
|
+
padRight5(env.type, typeWidth),
|
|
66992
|
+
padRight5(env.namespace || "-", namespaceWidth),
|
|
66367
66993
|
releaseDisplay
|
|
66368
66994
|
].join(" ");
|
|
66369
66995
|
console.log(row);
|
|
@@ -66455,21 +67081,21 @@ function formatEnvServices(report, services) {
|
|
|
66455
67081
|
const restartsWidth = Math.max(8, ...services.map((s) => String(s.restarts).length));
|
|
66456
67082
|
const phasesWidth = Math.max(6, ...services.map((s) => s.phases.join(", ").length));
|
|
66457
67083
|
const header = [
|
|
66458
|
-
|
|
66459
|
-
|
|
66460
|
-
|
|
66461
|
-
|
|
66462
|
-
|
|
67084
|
+
padRight5("Service", nameWidth),
|
|
67085
|
+
padRight5("Pods", totalWidth),
|
|
67086
|
+
padRight5("Ready", readyWidth),
|
|
67087
|
+
padRight5("Restarts", restartsWidth),
|
|
67088
|
+
padRight5("Phases", phasesWidth)
|
|
66463
67089
|
].join(" ");
|
|
66464
67090
|
console.log(header);
|
|
66465
67091
|
console.log("-".repeat(header.length));
|
|
66466
67092
|
for (const service of services) {
|
|
66467
67093
|
console.log([
|
|
66468
|
-
|
|
66469
|
-
|
|
66470
|
-
|
|
66471
|
-
|
|
66472
|
-
|
|
67094
|
+
padRight5(service.name, nameWidth),
|
|
67095
|
+
padRight5(String(service.pods_total), totalWidth),
|
|
67096
|
+
padRight5(String(service.pods_ready), readyWidth),
|
|
67097
|
+
padRight5(String(service.restarts), restartsWidth),
|
|
67098
|
+
padRight5(service.phases.join(", "), phasesWidth)
|
|
66473
67099
|
].join(" "));
|
|
66474
67100
|
}
|
|
66475
67101
|
}
|
|
@@ -66479,8 +67105,8 @@ function formatEnvServices(report, services) {
|
|
|
66479
67105
|
const nameWidth = Math.max(4, ...report.deployments.map((d) => d.name.length));
|
|
66480
67106
|
const readyWidth = Math.max(5, ...report.deployments.map((d) => `${d.available_replicas}/${d.desired_replicas}`.length));
|
|
66481
67107
|
const header = [
|
|
66482
|
-
|
|
66483
|
-
|
|
67108
|
+
padRight5("Name", nameWidth),
|
|
67109
|
+
padRight5("Ready", readyWidth),
|
|
66484
67110
|
"Status"
|
|
66485
67111
|
].join(" ");
|
|
66486
67112
|
console.log(header);
|
|
@@ -66489,8 +67115,8 @@ function formatEnvServices(report, services) {
|
|
|
66489
67115
|
const readiness = `${deployment.available_replicas}/${deployment.desired_replicas}`;
|
|
66490
67116
|
const status = deployment.ready ? "ready" : "not-ready";
|
|
66491
67117
|
console.log([
|
|
66492
|
-
|
|
66493
|
-
|
|
67118
|
+
padRight5(deployment.name, nameWidth),
|
|
67119
|
+
padRight5(readiness, readyWidth),
|
|
66494
67120
|
status
|
|
66495
67121
|
].join(" "));
|
|
66496
67122
|
}
|
|
@@ -66519,8 +67145,8 @@ function formatEnvDiagnose(report) {
|
|
|
66519
67145
|
const nameWidth = Math.max(4, ...report.deployments.map((d) => d.name.length));
|
|
66520
67146
|
const readyWidth = Math.max(5, ...report.deployments.map((d) => `${d.available_replicas}/${d.desired_replicas}`.length));
|
|
66521
67147
|
const header = [
|
|
66522
|
-
|
|
66523
|
-
|
|
67148
|
+
padRight5("Name", nameWidth),
|
|
67149
|
+
padRight5("Ready", readyWidth),
|
|
66524
67150
|
"Status"
|
|
66525
67151
|
].join(" ");
|
|
66526
67152
|
console.log(header);
|
|
@@ -66529,8 +67155,8 @@ function formatEnvDiagnose(report) {
|
|
|
66529
67155
|
const readiness = `${deployment.available_replicas}/${deployment.desired_replicas}`;
|
|
66530
67156
|
const status = deployment.ready ? "ready" : "not-ready";
|
|
66531
67157
|
console.log([
|
|
66532
|
-
|
|
66533
|
-
|
|
67158
|
+
padRight5(deployment.name, nameWidth),
|
|
67159
|
+
padRight5(readiness, readyWidth),
|
|
66534
67160
|
status
|
|
66535
67161
|
].join(" "));
|
|
66536
67162
|
}
|
|
@@ -66542,9 +67168,9 @@ function formatEnvDiagnose(report) {
|
|
|
66542
67168
|
const phaseWidth = Math.max(5, ...report.pods.map((p) => p.phase.length));
|
|
66543
67169
|
const restartsWidth = Math.max(8, ...report.pods.map((p) => String(p.restarts).length));
|
|
66544
67170
|
const header = [
|
|
66545
|
-
|
|
66546
|
-
|
|
66547
|
-
|
|
67171
|
+
padRight5("Name", nameWidth),
|
|
67172
|
+
padRight5("Phase", phaseWidth),
|
|
67173
|
+
padRight5("Restarts", restartsWidth),
|
|
66548
67174
|
"Ready",
|
|
66549
67175
|
"Age"
|
|
66550
67176
|
].join(" ");
|
|
@@ -66552,9 +67178,9 @@ function formatEnvDiagnose(report) {
|
|
|
66552
67178
|
console.log("-".repeat(header.length));
|
|
66553
67179
|
for (const pod of report.pods) {
|
|
66554
67180
|
console.log([
|
|
66555
|
-
|
|
66556
|
-
|
|
66557
|
-
|
|
67181
|
+
padRight5(pod.name, nameWidth),
|
|
67182
|
+
padRight5(pod.phase, phaseWidth),
|
|
67183
|
+
padRight5(String(pod.restarts), restartsWidth),
|
|
66558
67184
|
pod.ready ? "yes" : "no",
|
|
66559
67185
|
pod.age
|
|
66560
67186
|
].join(" "));
|
|
@@ -66654,7 +67280,7 @@ function formatDate2(dateStr) {
|
|
|
66654
67280
|
return dateStr;
|
|
66655
67281
|
}
|
|
66656
67282
|
}
|
|
66657
|
-
function
|
|
67283
|
+
function padRight5(str, width) {
|
|
66658
67284
|
if (str.length >= width) return str;
|
|
66659
67285
|
return str + " ".repeat(width - str.length);
|
|
66660
67286
|
}
|
|
@@ -67552,8 +68178,8 @@ function formatLifecycleMeta2(phase, meta) {
|
|
|
67552
68178
|
}
|
|
67553
68179
|
|
|
67554
68180
|
// src/commands/api.ts
|
|
67555
|
-
var
|
|
67556
|
-
var
|
|
68181
|
+
var import_node_fs8 = require("node:fs");
|
|
68182
|
+
var import_node_path8 = require("node:path");
|
|
67557
68183
|
var import_node_child_process6 = require("node:child_process");
|
|
67558
68184
|
var import_node_os4 = require("node:os");
|
|
67559
68185
|
async function handleApi(subcommand, positionals, flags, context2) {
|
|
@@ -67672,7 +68298,7 @@ async function handleExamples(positionals, flags, context2, jsonOutput) {
|
|
|
67672
68298
|
async function handleGenerate(positionals, flags, jsonOutput) {
|
|
67673
68299
|
const outDir = getStringFlag(flags, ["out"]) ?? positionals[0];
|
|
67674
68300
|
const repoRoot = resolveRepoRoot();
|
|
67675
|
-
const outputDir = outDir ? (0,
|
|
68301
|
+
const outputDir = outDir ? (0, import_node_path8.resolve)(repoRoot, outDir) : (0, import_node_path8.resolve)(repoRoot, "docs/system");
|
|
67676
68302
|
runOpenApiExport(repoRoot, outputDir);
|
|
67677
68303
|
outputJson({ ok: true, output_dir: outputDir }, jsonOutput, `OpenAPI exported to ${outputDir}`);
|
|
67678
68304
|
}
|
|
@@ -67680,13 +68306,13 @@ async function handleDiff(positionals, flags, jsonOutput) {
|
|
|
67680
68306
|
const exitCode = Boolean(flags["exit-code"]);
|
|
67681
68307
|
const repoRoot = resolveRepoRoot();
|
|
67682
68308
|
const expectedDir = getStringFlag(flags, ["out"]) ?? positionals[0];
|
|
67683
|
-
const targetDir = expectedDir ? (0,
|
|
67684
|
-
const tempDir = (0,
|
|
68309
|
+
const targetDir = expectedDir ? (0, import_node_path8.resolve)(repoRoot, expectedDir) : (0, import_node_path8.resolve)(repoRoot, "docs/system");
|
|
68310
|
+
const tempDir = (0, import_node_fs8.mkdtempSync)((0, import_node_path8.join)((0, import_node_os4.tmpdir)(), "eve-openapi-"));
|
|
67685
68311
|
try {
|
|
67686
68312
|
runOpenApiExport(repoRoot, tempDir);
|
|
67687
|
-
const actualPath = (0,
|
|
67688
|
-
const expectedPath = (0,
|
|
67689
|
-
if (!(0,
|
|
68313
|
+
const actualPath = (0, import_node_path8.resolve)(tempDir, "openapi.yaml");
|
|
68314
|
+
const expectedPath = (0, import_node_path8.resolve)(targetDir, "openapi.yaml");
|
|
68315
|
+
if (!(0, import_node_fs8.existsSync)(expectedPath)) {
|
|
67690
68316
|
throw new Error(`Missing expected OpenAPI spec at ${expectedPath}`);
|
|
67691
68317
|
}
|
|
67692
68318
|
const diff = (0, import_node_child_process6.spawnSync)("diff", ["-u", expectedPath, actualPath], { stdio: "inherit" });
|
|
@@ -67696,7 +68322,7 @@ async function handleDiff(positionals, flags, jsonOutput) {
|
|
|
67696
68322
|
}
|
|
67697
68323
|
outputJson({ ok: !hasDiff }, jsonOutput, hasDiff ? "OpenAPI spec drift detected" : "OpenAPI spec matches");
|
|
67698
68324
|
} finally {
|
|
67699
|
-
(0,
|
|
68325
|
+
(0, import_node_fs8.rmSync)(tempDir, { recursive: true, force: true });
|
|
67700
68326
|
}
|
|
67701
68327
|
}
|
|
67702
68328
|
async function handleCall(positionals, flags, context2) {
|
|
@@ -67985,10 +68611,10 @@ function stripTrailingSlash(url) {
|
|
|
67985
68611
|
function resolveRepoRoot() {
|
|
67986
68612
|
let current = process.cwd();
|
|
67987
68613
|
while (true) {
|
|
67988
|
-
if ((0,
|
|
68614
|
+
if ((0, import_node_fs8.existsSync)((0, import_node_path8.resolve)(current, "pnpm-workspace.yaml"))) {
|
|
67989
68615
|
return current;
|
|
67990
68616
|
}
|
|
67991
|
-
const parent = (0,
|
|
68617
|
+
const parent = (0, import_node_path8.dirname)(current);
|
|
67992
68618
|
if (parent === current) {
|
|
67993
68619
|
break;
|
|
67994
68620
|
}
|
|
@@ -67997,9 +68623,9 @@ function resolveRepoRoot() {
|
|
|
67997
68623
|
throw new Error("Unable to locate repo root (pnpm-workspace.yaml not found).");
|
|
67998
68624
|
}
|
|
67999
68625
|
function runOpenApiExport(repoRoot, outputDir) {
|
|
68000
|
-
const scriptPath = (0,
|
|
68001
|
-
if (!(0,
|
|
68002
|
-
const build = (0, import_node_child_process6.spawnSync)("pnpm", ["-C", (0,
|
|
68626
|
+
const scriptPath = (0, import_node_path8.resolve)(repoRoot, "apps/api/dist/scripts/export-openapi.js");
|
|
68627
|
+
if (!(0, import_node_fs8.existsSync)(scriptPath)) {
|
|
68628
|
+
const build = (0, import_node_child_process6.spawnSync)("pnpm", ["-C", (0, import_node_path8.resolve)(repoRoot, "apps/api"), "build"], { stdio: "inherit" });
|
|
68003
68629
|
if (build.status !== 0) {
|
|
68004
68630
|
throw new Error("Failed to build API before exporting OpenAPI spec");
|
|
68005
68631
|
}
|
|
@@ -68035,14 +68661,14 @@ function resolveJsonInput(value) {
|
|
|
68035
68661
|
}
|
|
68036
68662
|
function resolveTextInput(value) {
|
|
68037
68663
|
if (value.startsWith("@")) {
|
|
68038
|
-
const filePath = (0,
|
|
68039
|
-
return (0,
|
|
68664
|
+
const filePath = (0, import_node_path8.resolve)(value.slice(1));
|
|
68665
|
+
return (0, import_node_fs8.readFileSync)(filePath, "utf-8");
|
|
68040
68666
|
}
|
|
68041
68667
|
if (value.trim().startsWith("{") || value.trim().startsWith("[")) {
|
|
68042
68668
|
return value;
|
|
68043
68669
|
}
|
|
68044
|
-
if ((0,
|
|
68045
|
-
return (0,
|
|
68670
|
+
if ((0, import_node_fs8.existsSync)(value)) {
|
|
68671
|
+
return (0, import_node_fs8.readFileSync)((0, import_node_path8.resolve)(value), "utf-8");
|
|
68046
68672
|
}
|
|
68047
68673
|
return value;
|
|
68048
68674
|
}
|
|
@@ -68085,8 +68711,8 @@ function buildCurlCommand(options) {
|
|
|
68085
68711
|
}
|
|
68086
68712
|
|
|
68087
68713
|
// src/commands/db.ts
|
|
68088
|
-
var
|
|
68089
|
-
var
|
|
68714
|
+
var import_node_fs9 = require("node:fs");
|
|
68715
|
+
var import_node_path9 = require("node:path");
|
|
68090
68716
|
var MIGRATION_REGEX = /^(\d{14})_([a-z0-9_]+)\.sql$/;
|
|
68091
68717
|
async function handleDb(subcommand, positionals, flags, context2) {
|
|
68092
68718
|
const jsonOutput = Boolean(flags.json);
|
|
@@ -68104,7 +68730,7 @@ async function handleDb(subcommand, positionals, flags, context2) {
|
|
|
68104
68730
|
case "new":
|
|
68105
68731
|
return handleNew(positionals, flags);
|
|
68106
68732
|
case "status":
|
|
68107
|
-
return
|
|
68733
|
+
return handleStatus3(positionals, flags, context2, jsonOutput);
|
|
68108
68734
|
case "rotate-credentials":
|
|
68109
68735
|
return handleRotateCredentials(positionals, flags, context2, jsonOutput);
|
|
68110
68736
|
case "scale":
|
|
@@ -68113,7 +68739,7 @@ async function handleDb(subcommand, positionals, flags, context2) {
|
|
|
68113
68739
|
return handleDestroy(positionals, flags, context2, jsonOutput);
|
|
68114
68740
|
default:
|
|
68115
68741
|
throw new Error(
|
|
68116
|
-
"Usage: eve db <command> [options]\n\nCommands:\n schema --env <name> Show DB schema info\n rls --env <name> Show RLS policies and tables\n sql --env <name> --sql <stmt> Run parameterized SQL\n migrate --env <name> [--path <dir>] Apply pending migrations\n migrations --env <name> List applied migrations\n new <description> Create new migration file\n status --env <name> Show managed DB status\n rotate-credentials --env <name> Rotate managed DB credentials\n scale --env <name> --class <cls> Scale managed DB class\n destroy --env <name> --force Destroy managed DB"
|
|
68742
|
+
"Usage: eve db <command> [options]\n\nCommands:\n schema --env <name> Show DB schema info\n rls --env <name> Show RLS policies and tables\n rls init --with-groups Scaffold group-aware RLS helper SQL\n sql --env <name> --sql <stmt> Run parameterized SQL\n migrate --env <name> [--path <dir>] Apply pending migrations\n migrations --env <name> List applied migrations\n new <description> Create new migration file\n status --env <name> Show managed DB status\n rotate-credentials --env <name> Rotate managed DB credentials\n scale --env <name> --class <cls> Scale managed DB class\n destroy --env <name> --force Destroy managed DB"
|
|
68117
68743
|
);
|
|
68118
68744
|
}
|
|
68119
68745
|
}
|
|
@@ -68123,9 +68749,109 @@ async function handleSchema(positionals, flags, context2, jsonOutput) {
|
|
|
68123
68749
|
outputJson(response, jsonOutput);
|
|
68124
68750
|
}
|
|
68125
68751
|
async function handleRls(positionals, flags, context2, jsonOutput) {
|
|
68752
|
+
if (positionals[0] === "init") {
|
|
68753
|
+
return handleRlsInit(flags, jsonOutput);
|
|
68754
|
+
}
|
|
68126
68755
|
const { projectId, envName } = resolveProjectEnv(positionals, flags, context2);
|
|
68127
68756
|
const response = await requestJson(context2, `/projects/${projectId}/envs/${envName}/db/rls`);
|
|
68128
|
-
|
|
68757
|
+
if (jsonOutput) {
|
|
68758
|
+
outputJson(response, true);
|
|
68759
|
+
return;
|
|
68760
|
+
}
|
|
68761
|
+
printRlsResponse(response);
|
|
68762
|
+
}
|
|
68763
|
+
function handleRlsInit(flags, jsonOutput) {
|
|
68764
|
+
const withGroups = toBoolean(flags["with-groups"]) ?? false;
|
|
68765
|
+
const force = toBoolean(flags.force) ?? false;
|
|
68766
|
+
if (!withGroups) {
|
|
68767
|
+
throw new Error("Usage: eve db rls init --with-groups [--out <path>] [--force]");
|
|
68768
|
+
}
|
|
68769
|
+
const outPath = getStringFlag(flags, ["out"]) ?? "db/rls/helpers.sql";
|
|
68770
|
+
const fullPath = (0, import_node_path9.resolve)(outPath);
|
|
68771
|
+
if ((0, import_node_fs9.existsSync)(fullPath) && !force) {
|
|
68772
|
+
throw new Error(`RLS helper file already exists: ${fullPath}
|
|
68773
|
+
Use --force to overwrite.`);
|
|
68774
|
+
}
|
|
68775
|
+
(0, import_node_fs9.mkdirSync)((0, import_node_path9.dirname)(fullPath), { recursive: true });
|
|
68776
|
+
(0, import_node_fs9.writeFileSync)(fullPath, renderRlsHelperTemplate());
|
|
68777
|
+
const payload = {
|
|
68778
|
+
path: fullPath,
|
|
68779
|
+
with_groups: true
|
|
68780
|
+
};
|
|
68781
|
+
outputJson(payload, jsonOutput, `Created group-aware RLS helpers at ${fullPath}`);
|
|
68782
|
+
}
|
|
68783
|
+
function renderRlsHelperTemplate() {
|
|
68784
|
+
return `-- Eve RLS helpers generated by "eve db rls init --with-groups"
|
|
68785
|
+
-- Usage:
|
|
68786
|
+
-- 1. Apply this SQL in your target environment DB.
|
|
68787
|
+
-- 2. Reference app.current_user_id()/app.current_group_ids()/app.has_group() in policies.
|
|
68788
|
+
--
|
|
68789
|
+
-- Example:
|
|
68790
|
+
-- CREATE POLICY notes_group_read
|
|
68791
|
+
-- ON notes
|
|
68792
|
+
-- FOR SELECT
|
|
68793
|
+
-- USING (group_id = ANY(app.current_group_ids()));
|
|
68794
|
+
|
|
68795
|
+
CREATE SCHEMA IF NOT EXISTS app;
|
|
68796
|
+
|
|
68797
|
+
CREATE OR REPLACE FUNCTION app.current_user_id()
|
|
68798
|
+
RETURNS text
|
|
68799
|
+
LANGUAGE sql
|
|
68800
|
+
STABLE
|
|
68801
|
+
AS $$
|
|
68802
|
+
SELECT NULLIF(current_setting('app.user_id', true), '');
|
|
68803
|
+
$$;
|
|
68804
|
+
|
|
68805
|
+
CREATE OR REPLACE FUNCTION app.current_group_ids()
|
|
68806
|
+
RETURNS text[]
|
|
68807
|
+
LANGUAGE sql
|
|
68808
|
+
STABLE
|
|
68809
|
+
AS $$
|
|
68810
|
+
WITH raw AS (
|
|
68811
|
+
SELECT NULLIF(current_setting('app.group_ids', true), '') AS value
|
|
68812
|
+
)
|
|
68813
|
+
SELECT COALESCE(
|
|
68814
|
+
ARRAY(
|
|
68815
|
+
SELECT jsonb_array_elements_text(COALESCE(raw.value::jsonb, '[]'::jsonb))
|
|
68816
|
+
FROM raw
|
|
68817
|
+
),
|
|
68818
|
+
ARRAY[]::text[]
|
|
68819
|
+
);
|
|
68820
|
+
$$;
|
|
68821
|
+
|
|
68822
|
+
CREATE OR REPLACE FUNCTION app.has_group(group_id text)
|
|
68823
|
+
RETURNS boolean
|
|
68824
|
+
LANGUAGE sql
|
|
68825
|
+
STABLE
|
|
68826
|
+
AS $$
|
|
68827
|
+
SELECT group_id = ANY(app.current_group_ids());
|
|
68828
|
+
$$;
|
|
68829
|
+
`;
|
|
68830
|
+
}
|
|
68831
|
+
function printRlsResponse(response) {
|
|
68832
|
+
const diagnostics = response.diagnostics?.context;
|
|
68833
|
+
if (diagnostics) {
|
|
68834
|
+
const groups = diagnostics.group_ids ?? [];
|
|
68835
|
+
const permissions = diagnostics.permissions ?? [];
|
|
68836
|
+
console.log("Context:");
|
|
68837
|
+
console.log(` Principal: ${diagnostics.principal_type ?? "unknown"} ${diagnostics.user_id ?? "(none)"}`);
|
|
68838
|
+
console.log(` Org: ${diagnostics.org_id ?? "(none)"}`);
|
|
68839
|
+
console.log(` Project: ${diagnostics.project_id ?? "(none)"}`);
|
|
68840
|
+
console.log(` Env: ${diagnostics.env_name ?? "(none)"}`);
|
|
68841
|
+
console.log(` Groups (${groups.length}): ${groups.length > 0 ? groups.join(", ") : "(none)"}`);
|
|
68842
|
+
console.log(` Permissions (${permissions.length}): ${permissions.length > 0 ? permissions.join(", ") : "(none)"}`);
|
|
68843
|
+
console.log("");
|
|
68844
|
+
}
|
|
68845
|
+
const tables = response.tables ?? [];
|
|
68846
|
+
if (tables.length === 0) {
|
|
68847
|
+
console.log("No user tables/views found.");
|
|
68848
|
+
return;
|
|
68849
|
+
}
|
|
68850
|
+
console.log(`RLS tables (${tables.length}):`);
|
|
68851
|
+
for (const table of tables) {
|
|
68852
|
+
const state = table.rls_enabled ? "enabled" : "disabled";
|
|
68853
|
+
console.log(` - ${table.schema}.${table.name} (${state}, ${table.policies.length} policies)`);
|
|
68854
|
+
}
|
|
68129
68855
|
}
|
|
68130
68856
|
async function handleSql(positionals, flags, context2, jsonOutput) {
|
|
68131
68857
|
const projectId = getStringFlag(flags, ["project"]) ?? context2.projectId;
|
|
@@ -68137,7 +68863,7 @@ async function handleSql(positionals, flags, context2, jsonOutput) {
|
|
|
68137
68863
|
const sqlPositionals = envFromFlag ? positionals : positionals.slice(1);
|
|
68138
68864
|
const sqlInput = getStringFlag(flags, ["sql"]) ?? sqlPositionals.join(" ");
|
|
68139
68865
|
const fileInput = getStringFlag(flags, ["file"]);
|
|
68140
|
-
const sqlText = fileInput ? (0,
|
|
68866
|
+
const sqlText = fileInput ? (0, import_node_fs9.readFileSync)((0, import_node_path9.resolve)(fileInput), "utf-8") : sqlInput;
|
|
68141
68867
|
if (!sqlText) {
|
|
68142
68868
|
throw new Error("Usage: eve db sql --env <name> --sql <statement> [--params <json>] [--write]");
|
|
68143
68869
|
}
|
|
@@ -68176,11 +68902,11 @@ function parseJson(value, label) {
|
|
|
68176
68902
|
async function handleMigrate(positionals, flags, context2, jsonOutput) {
|
|
68177
68903
|
const { projectId, envName } = resolveProjectEnv(positionals, flags, context2);
|
|
68178
68904
|
const migrationsPath = getStringFlag(flags, ["path"]) ?? "db/migrations";
|
|
68179
|
-
const fullPath = (0,
|
|
68180
|
-
if (!(0,
|
|
68905
|
+
const fullPath = (0, import_node_path9.resolve)(migrationsPath);
|
|
68906
|
+
if (!(0, import_node_fs9.existsSync)(fullPath)) {
|
|
68181
68907
|
throw new Error(`Migrations directory not found: ${fullPath}`);
|
|
68182
68908
|
}
|
|
68183
|
-
const files = (0,
|
|
68909
|
+
const files = (0, import_node_fs9.readdirSync)(fullPath).filter((f) => f.endsWith(".sql")).sort();
|
|
68184
68910
|
if (files.length === 0) {
|
|
68185
68911
|
console.log("No migration files found");
|
|
68186
68912
|
return;
|
|
@@ -68196,7 +68922,7 @@ Example: 20260128100000_create_users.sql`
|
|
|
68196
68922
|
}
|
|
68197
68923
|
const migrations = files.map((file) => ({
|
|
68198
68924
|
name: file,
|
|
68199
|
-
sql: (0,
|
|
68925
|
+
sql: (0, import_node_fs9.readFileSync)((0, import_node_path9.join)(fullPath, file), "utf-8")
|
|
68200
68926
|
}));
|
|
68201
68927
|
console.log(`Found ${migrations.length} migration files`);
|
|
68202
68928
|
const response = await requestJson(context2, `/projects/${projectId}/envs/${envName}/db/migrate`, {
|
|
@@ -68258,12 +68984,12 @@ function handleNew(positionals, flags) {
|
|
|
68258
68984
|
].join("");
|
|
68259
68985
|
const filename = `${timestamp}_${normalizedDescription}.sql`;
|
|
68260
68986
|
const migrationsPath = getStringFlag(flags, ["path"]) ?? "db/migrations";
|
|
68261
|
-
const fullPath = (0,
|
|
68262
|
-
if (!(0,
|
|
68263
|
-
(0,
|
|
68987
|
+
const fullPath = (0, import_node_path9.resolve)(migrationsPath);
|
|
68988
|
+
if (!(0, import_node_fs9.existsSync)(fullPath)) {
|
|
68989
|
+
(0, import_node_fs9.mkdirSync)(fullPath, { recursive: true });
|
|
68264
68990
|
}
|
|
68265
|
-
const filePath = (0,
|
|
68266
|
-
if ((0,
|
|
68991
|
+
const filePath = (0, import_node_path9.join)(fullPath, filename);
|
|
68992
|
+
if ((0, import_node_fs9.existsSync)(filePath)) {
|
|
68267
68993
|
throw new Error(`Migration file already exists: ${filePath}`);
|
|
68268
68994
|
}
|
|
68269
68995
|
const template = `-- Migration: ${normalizedDescription}
|
|
@@ -68272,10 +68998,10 @@ function handleNew(positionals, flags) {
|
|
|
68272
68998
|
-- Write your SQL migration here
|
|
68273
68999
|
|
|
68274
69000
|
`;
|
|
68275
|
-
(0,
|
|
69001
|
+
(0, import_node_fs9.writeFileSync)(filePath, template);
|
|
68276
69002
|
console.log(`Created: ${filePath}`);
|
|
68277
69003
|
}
|
|
68278
|
-
async function
|
|
69004
|
+
async function handleStatus3(positionals, flags, context2, jsonOutput) {
|
|
68279
69005
|
const { projectId, envName } = resolveProjectEnv(positionals, flags, context2);
|
|
68280
69006
|
const response = await requestJson(context2, `/projects/${projectId}/envs/${envName}/db/managed`);
|
|
68281
69007
|
if (jsonOutput) {
|
|
@@ -68509,24 +69235,24 @@ function formatEventsTable(events) {
|
|
|
68509
69235
|
const sourceWidth = Math.max(6, ...events.map((e) => e.source.length));
|
|
68510
69236
|
const statusWidth = Math.max(6, ...events.map((e) => e.status.length));
|
|
68511
69237
|
const header = [
|
|
68512
|
-
|
|
68513
|
-
|
|
68514
|
-
|
|
68515
|
-
|
|
68516
|
-
|
|
68517
|
-
|
|
69238
|
+
padRight6("ID", idWidth),
|
|
69239
|
+
padRight6("Type", typeWidth),
|
|
69240
|
+
padRight6("Source", sourceWidth),
|
|
69241
|
+
padRight6("Status", statusWidth),
|
|
69242
|
+
padRight6("Env", 12),
|
|
69243
|
+
padRight6("Branch", 20),
|
|
68518
69244
|
"Created"
|
|
68519
69245
|
].join(" ");
|
|
68520
69246
|
console.log(header);
|
|
68521
69247
|
console.log("-".repeat(header.length));
|
|
68522
69248
|
for (const event of events) {
|
|
68523
69249
|
const row = [
|
|
68524
|
-
|
|
68525
|
-
|
|
68526
|
-
|
|
68527
|
-
|
|
68528
|
-
|
|
68529
|
-
|
|
69250
|
+
padRight6(event.id, idWidth),
|
|
69251
|
+
padRight6(event.type, typeWidth),
|
|
69252
|
+
padRight6(event.source, sourceWidth),
|
|
69253
|
+
padRight6(event.status, statusWidth),
|
|
69254
|
+
padRight6(event.env_name || "-", 12),
|
|
69255
|
+
padRight6(event.ref_branch || "-", 20),
|
|
68530
69256
|
formatDate3(event.created_at)
|
|
68531
69257
|
].join(" ");
|
|
68532
69258
|
console.log(row);
|
|
@@ -68579,7 +69305,7 @@ function formatDate3(dateStr) {
|
|
|
68579
69305
|
return dateStr;
|
|
68580
69306
|
}
|
|
68581
69307
|
}
|
|
68582
|
-
function
|
|
69308
|
+
function padRight6(str, width) {
|
|
68583
69309
|
if (str.length >= width) return str;
|
|
68584
69310
|
return str + " ".repeat(width - str.length);
|
|
68585
69311
|
}
|
|
@@ -69391,11 +70117,11 @@ async function fetchGitHubKeys2(username) {
|
|
|
69391
70117
|
|
|
69392
70118
|
// src/commands/agents.ts
|
|
69393
70119
|
var import_node_child_process8 = require("node:child_process");
|
|
69394
|
-
var
|
|
69395
|
-
var
|
|
70120
|
+
var import_node_fs10 = require("node:fs");
|
|
70121
|
+
var import_node_path10 = require("node:path");
|
|
69396
70122
|
var import_yaml3 = require("yaml");
|
|
69397
70123
|
function readYamlFile(filePath) {
|
|
69398
|
-
const raw = (0,
|
|
70124
|
+
const raw = (0, import_node_fs10.readFileSync)(filePath, "utf-8");
|
|
69399
70125
|
const parsed = (0, import_yaml3.parse)(raw);
|
|
69400
70126
|
if (!parsed || typeof parsed !== "object") {
|
|
69401
70127
|
throw new Error(`Invalid YAML in ${filePath}`);
|
|
@@ -69407,9 +70133,9 @@ function resolveAgentsConfigPaths(repoRoot, manifest) {
|
|
|
69407
70133
|
const agentsBlock = xEve["agents"] || {};
|
|
69408
70134
|
const chatBlock = xEve["chat"] || {};
|
|
69409
70135
|
const manifestChat = manifest["chat"] || {};
|
|
69410
|
-
const agentsPath = (0,
|
|
69411
|
-
const teamsPath = (0,
|
|
69412
|
-
const chatPath = (0,
|
|
70136
|
+
const agentsPath = (0, import_node_path10.resolve)(repoRoot, pickString(agentsBlock.config_path) ?? "agents/agents.yaml");
|
|
70137
|
+
const teamsPath = (0, import_node_path10.resolve)(repoRoot, pickString(agentsBlock.teams_path) ?? "agents/teams.yaml");
|
|
70138
|
+
const chatPath = (0, import_node_path10.resolve)(
|
|
69413
70139
|
repoRoot,
|
|
69414
70140
|
pickString(chatBlock.config_path) ?? pickString(manifestChat.config_path) ?? "agents/chat.yaml"
|
|
69415
70141
|
);
|
|
@@ -69419,7 +70145,7 @@ function pickString(value) {
|
|
|
69419
70145
|
return typeof value === "string" && value.trim().length > 0 ? value : void 0;
|
|
69420
70146
|
}
|
|
69421
70147
|
function ensureFileExists(path6, label) {
|
|
69422
|
-
if (!(0,
|
|
70148
|
+
if (!(0, import_node_fs10.existsSync)(path6)) {
|
|
69423
70149
|
throw new Error(`Missing ${label} at ${path6}. Update manifest config_path or add the file.`);
|
|
69424
70150
|
}
|
|
69425
70151
|
return path6;
|
|
@@ -69434,9 +70160,9 @@ function isLocalApiUrl(apiUrl) {
|
|
|
69434
70160
|
}
|
|
69435
70161
|
}
|
|
69436
70162
|
function loadAgentsConfig(repoRoot) {
|
|
69437
|
-
const eveDir = (0,
|
|
69438
|
-
const manifestPath = (0,
|
|
69439
|
-
if ((0,
|
|
70163
|
+
const eveDir = (0, import_node_path10.join)(repoRoot, ".eve");
|
|
70164
|
+
const manifestPath = (0, import_node_path10.join)(eveDir, "manifest.yaml");
|
|
70165
|
+
if ((0, import_node_fs10.existsSync)(manifestPath)) {
|
|
69440
70166
|
const manifest = readYamlFile(manifestPath);
|
|
69441
70167
|
const xEve = manifest["x-eve"] || manifest["x_eve"] || {};
|
|
69442
70168
|
const policy = xEve["agents"] || null;
|
|
@@ -69486,16 +70212,16 @@ async function resolvePacksAndMerge(repoRoot, manifest, projectSlug) {
|
|
|
69486
70212
|
}
|
|
69487
70213
|
}
|
|
69488
70214
|
const configPaths = resolveAgentsConfigPaths(repoRoot, manifest);
|
|
69489
|
-
if ((0,
|
|
69490
|
-
const projectAgents = (0, import_yaml3.parse)((0,
|
|
70215
|
+
if ((0, import_node_fs10.existsSync)(configPaths.agentsPath)) {
|
|
70216
|
+
const projectAgents = (0, import_yaml3.parse)((0, import_node_fs10.readFileSync)(configPaths.agentsPath, "utf-8")) ?? {};
|
|
69491
70217
|
mergedAgents = mergeMapConfig(mergedAgents, projectAgents);
|
|
69492
70218
|
}
|
|
69493
|
-
if ((0,
|
|
69494
|
-
const projectTeams = (0, import_yaml3.parse)((0,
|
|
70219
|
+
if ((0, import_node_fs10.existsSync)(configPaths.teamsPath)) {
|
|
70220
|
+
const projectTeams = (0, import_yaml3.parse)((0, import_node_fs10.readFileSync)(configPaths.teamsPath, "utf-8")) ?? {};
|
|
69495
70221
|
mergedTeams = mergeMapConfig(mergedTeams, projectTeams);
|
|
69496
70222
|
}
|
|
69497
|
-
if ((0,
|
|
69498
|
-
const projectChat = (0, import_yaml3.parse)((0,
|
|
70223
|
+
if ((0, import_node_fs10.existsSync)(configPaths.chatPath)) {
|
|
70224
|
+
const projectChat = (0, import_yaml3.parse)((0, import_node_fs10.readFileSync)(configPaths.chatPath, "utf-8")) ?? {};
|
|
69499
70225
|
mergedChat = mergeChatConfig(
|
|
69500
70226
|
mergedChat,
|
|
69501
70227
|
projectChat
|
|
@@ -69524,10 +70250,10 @@ async function resolvePacksAndMerge(repoRoot, manifest, projectSlug) {
|
|
|
69524
70250
|
chat_hash: simpleHash(JSON.stringify(mergedChat))
|
|
69525
70251
|
}
|
|
69526
70252
|
};
|
|
69527
|
-
const eveDir = (0,
|
|
69528
|
-
(0,
|
|
69529
|
-
const lockfilePath = (0,
|
|
69530
|
-
(0,
|
|
70253
|
+
const eveDir = (0, import_node_path10.join)(repoRoot, ".eve");
|
|
70254
|
+
(0, import_node_fs10.mkdirSync)(eveDir, { recursive: true });
|
|
70255
|
+
const lockfilePath = (0, import_node_path10.join)(eveDir, "packs.lock.yaml");
|
|
70256
|
+
(0, import_node_fs10.writeFileSync)(lockfilePath, (0, import_yaml3.stringify)(lockfile), "utf-8");
|
|
69531
70257
|
console.log(` \u2713 Lockfile written: .eve/packs.lock.yaml`);
|
|
69532
70258
|
const packRefs = resolvedPacks.map((p) => ({ id: p.id, source: p.source, ref: p.ref }));
|
|
69533
70259
|
return {
|
|
@@ -69600,7 +70326,7 @@ async function handleAgents(subcommand, positionals, flags, context2) {
|
|
|
69600
70326
|
const command = subcommand ?? "config";
|
|
69601
70327
|
const json = Boolean(flags.json);
|
|
69602
70328
|
const includeHarnesses = !(getBooleanFlag(flags, ["no-harnesses"]) ?? false);
|
|
69603
|
-
const repoRoot = (0,
|
|
70329
|
+
const repoRoot = (0, import_node_path10.resolve)(getStringFlag(flags, ["repo-dir", "repo_dir", "dir", "path"]) ?? process.cwd());
|
|
69604
70330
|
switch (command) {
|
|
69605
70331
|
case "config": {
|
|
69606
70332
|
const result = loadAgentsConfig(repoRoot);
|
|
@@ -69669,8 +70395,8 @@ async function handleAgents(subcommand, positionals, flags, context2) {
|
|
|
69669
70395
|
if (dirty && !allowDirty) {
|
|
69670
70396
|
throw new Error("Working tree is dirty. Commit changes or pass --allow-dirty to sync anyway.");
|
|
69671
70397
|
}
|
|
69672
|
-
const manifestPath = (0,
|
|
69673
|
-
if (!(0,
|
|
70398
|
+
const manifestPath = (0, import_node_path10.join)(repoRoot, ".eve", "manifest.yaml");
|
|
70399
|
+
if (!(0, import_node_fs10.existsSync)(manifestPath)) {
|
|
69674
70400
|
throw new Error(`Missing manifest at ${manifestPath}. Expected .eve/manifest.yaml.`);
|
|
69675
70401
|
}
|
|
69676
70402
|
const manifest = readYamlFile(manifestPath);
|
|
@@ -69699,9 +70425,9 @@ async function handleAgents(subcommand, positionals, flags, context2) {
|
|
|
69699
70425
|
packRefs = packResult.packRefs;
|
|
69700
70426
|
} else {
|
|
69701
70427
|
const configPaths = resolveAgentsConfigPaths(repoRoot, manifest);
|
|
69702
|
-
agentsYaml = (0,
|
|
69703
|
-
teamsYaml = (0,
|
|
69704
|
-
chatYaml = (0,
|
|
70428
|
+
agentsYaml = (0, import_node_fs10.readFileSync)(ensureFileExists(configPaths.agentsPath, "agents config"), "utf-8");
|
|
70429
|
+
teamsYaml = (0, import_node_fs10.readFileSync)(ensureFileExists(configPaths.teamsPath, "teams config"), "utf-8");
|
|
70430
|
+
chatYaml = (0, import_node_fs10.readFileSync)(ensureFileExists(configPaths.chatPath, "chat config"), "utf-8");
|
|
69705
70431
|
}
|
|
69706
70432
|
let gitSha;
|
|
69707
70433
|
let branch;
|
|
@@ -69784,10 +70510,10 @@ function formatAgentRuntimeStatus(response, orgId) {
|
|
|
69784
70510
|
const ageWidth = Math.max(3, ...response.pods.map((pod) => formatAgeSeconds(pod.last_heartbeat_at).length));
|
|
69785
70511
|
console.log("");
|
|
69786
70512
|
const header = [
|
|
69787
|
-
|
|
69788
|
-
|
|
69789
|
-
|
|
69790
|
-
|
|
70513
|
+
padRight7("Pod", nameWidth),
|
|
70514
|
+
padRight7("Status", statusWidth),
|
|
70515
|
+
padRight7("Capacity", capacityWidth),
|
|
70516
|
+
padRight7("Age", ageWidth),
|
|
69791
70517
|
"Last Heartbeat"
|
|
69792
70518
|
].join(" ");
|
|
69793
70519
|
console.log(header);
|
|
@@ -69795,15 +70521,15 @@ function formatAgentRuntimeStatus(response, orgId) {
|
|
|
69795
70521
|
for (const pod of response.pods) {
|
|
69796
70522
|
const age = formatAgeSeconds(pod.last_heartbeat_at);
|
|
69797
70523
|
console.log([
|
|
69798
|
-
|
|
69799
|
-
|
|
69800
|
-
|
|
69801
|
-
|
|
70524
|
+
padRight7(pod.pod_name, nameWidth),
|
|
70525
|
+
padRight7(pod.status, statusWidth),
|
|
70526
|
+
padRight7(String(pod.capacity), capacityWidth),
|
|
70527
|
+
padRight7(age, ageWidth),
|
|
69802
70528
|
pod.last_heartbeat_at
|
|
69803
70529
|
].join(" "));
|
|
69804
70530
|
}
|
|
69805
70531
|
}
|
|
69806
|
-
function
|
|
70532
|
+
function padRight7(value, width) {
|
|
69807
70533
|
return value.length >= width ? value : `${value}${" ".repeat(width - value.length)}`;
|
|
69808
70534
|
}
|
|
69809
70535
|
function formatAgeSeconds(isoDate) {
|
|
@@ -70044,8 +70770,8 @@ function ensureSkillsSymlink2(projectRoot) {
|
|
|
70044
70770
|
}
|
|
70045
70771
|
|
|
70046
70772
|
// src/commands/release.ts
|
|
70047
|
-
var
|
|
70048
|
-
var
|
|
70773
|
+
var import_node_fs11 = require("node:fs");
|
|
70774
|
+
var import_node_path11 = require("node:path");
|
|
70049
70775
|
async function handleRelease2(subcommand, positionals, flags, context2) {
|
|
70050
70776
|
const json = Boolean(flags.json);
|
|
70051
70777
|
switch (subcommand) {
|
|
@@ -70057,9 +70783,9 @@ async function handleRelease2(subcommand, positionals, flags, context2) {
|
|
|
70057
70783
|
let projectId = typeof flags.project === "string" ? flags.project : context2.projectId;
|
|
70058
70784
|
if (!projectId) {
|
|
70059
70785
|
const dir = typeof flags.dir === "string" ? flags.dir : process.cwd();
|
|
70060
|
-
const manifestPath = (0,
|
|
70786
|
+
const manifestPath = (0, import_node_path11.join)(dir, ".eve", "manifest.yaml");
|
|
70061
70787
|
try {
|
|
70062
|
-
const yaml = (0,
|
|
70788
|
+
const yaml = (0, import_node_fs11.readFileSync)(manifestPath, "utf-8");
|
|
70063
70789
|
const projectMatch = yaml.match(/^project:\s*(\S+)/m);
|
|
70064
70790
|
if (projectMatch) {
|
|
70065
70791
|
projectId = projectMatch[1];
|
|
@@ -70108,8 +70834,8 @@ Make sure the release exists and the tag is correct.`
|
|
|
70108
70834
|
}
|
|
70109
70835
|
|
|
70110
70836
|
// src/commands/manifest.ts
|
|
70111
|
-
var
|
|
70112
|
-
var
|
|
70837
|
+
var import_node_fs12 = require("node:fs");
|
|
70838
|
+
var import_node_path12 = require("node:path");
|
|
70113
70839
|
async function handleManifest(subcommand, positionals, flags, context2) {
|
|
70114
70840
|
const json = Boolean(flags.json);
|
|
70115
70841
|
switch (subcommand) {
|
|
@@ -70119,11 +70845,11 @@ async function handleManifest(subcommand, positionals, flags, context2) {
|
|
|
70119
70845
|
const validateSecretsFlag = flags["validate-secrets"] ?? flags.validate_secrets;
|
|
70120
70846
|
const validateSecrets = toBoolean(validateSecretsFlag) ?? false;
|
|
70121
70847
|
const dir = typeof flags.dir === "string" ? flags.dir : process.cwd();
|
|
70122
|
-
const manifestPath = getStringFlag(flags, ["path"]) ?? (0,
|
|
70848
|
+
const manifestPath = getStringFlag(flags, ["path"]) ?? (0, import_node_path12.join)(dir, ".eve", "manifest.yaml");
|
|
70123
70849
|
let manifestYaml;
|
|
70124
70850
|
if (!useLatest) {
|
|
70125
70851
|
try {
|
|
70126
|
-
manifestYaml = (0,
|
|
70852
|
+
manifestYaml = (0, import_node_fs12.readFileSync)(manifestPath, "utf-8");
|
|
70127
70853
|
} catch (error) {
|
|
70128
70854
|
throw new Error(`Failed to read manifest at ${manifestPath}: ${error.message}`);
|
|
70129
70855
|
}
|
|
@@ -70191,11 +70917,11 @@ async function handleManifest(subcommand, positionals, flags, context2) {
|
|
|
70191
70917
|
}
|
|
70192
70918
|
|
|
70193
70919
|
// src/commands/packs.ts
|
|
70194
|
-
var
|
|
70195
|
-
var
|
|
70920
|
+
var import_node_fs13 = require("node:fs");
|
|
70921
|
+
var import_node_path13 = require("node:path");
|
|
70196
70922
|
var import_yaml4 = require("yaml");
|
|
70197
70923
|
async function handlePacks(subcommand, _rest, flags, _context) {
|
|
70198
|
-
const repoRoot = (0,
|
|
70924
|
+
const repoRoot = (0, import_node_path13.resolve)(getStringFlag(flags, ["repo-dir", "repo_dir", "dir", "path"]) ?? process.cwd());
|
|
70199
70925
|
switch (subcommand) {
|
|
70200
70926
|
case "status": {
|
|
70201
70927
|
printPacksStatus(repoRoot);
|
|
@@ -70211,14 +70937,14 @@ async function handlePacks(subcommand, _rest, flags, _context) {
|
|
|
70211
70937
|
}
|
|
70212
70938
|
}
|
|
70213
70939
|
function printPacksStatus(repoRoot) {
|
|
70214
|
-
const lockfilePath = (0,
|
|
70215
|
-
const manifestPath = (0,
|
|
70216
|
-
if (!(0,
|
|
70940
|
+
const lockfilePath = (0, import_node_path13.join)(repoRoot, ".eve", "packs.lock.yaml");
|
|
70941
|
+
const manifestPath = (0, import_node_path13.join)(repoRoot, ".eve", "manifest.yaml");
|
|
70942
|
+
if (!(0, import_node_fs13.existsSync)(lockfilePath)) {
|
|
70217
70943
|
console.log("No lockfile found at .eve/packs.lock.yaml");
|
|
70218
70944
|
console.log('Run "eve agents sync" to resolve packs and generate the lockfile.');
|
|
70219
70945
|
return;
|
|
70220
70946
|
}
|
|
70221
|
-
const lockRaw = (0,
|
|
70947
|
+
const lockRaw = (0, import_node_fs13.readFileSync)(lockfilePath, "utf-8");
|
|
70222
70948
|
const lock = (0, import_yaml4.parse)(lockRaw);
|
|
70223
70949
|
if (!lock || !lock.packs) {
|
|
70224
70950
|
console.log("Lockfile is empty or malformed.");
|
|
@@ -70234,16 +70960,16 @@ function printPacksStatus(repoRoot) {
|
|
|
70234
70960
|
const idWidth = Math.max(4, ...lock.packs.map((p) => p.id.length));
|
|
70235
70961
|
const sourceWidth = Math.max(6, ...lock.packs.map((p) => p.source.length));
|
|
70236
70962
|
const header = [
|
|
70237
|
-
|
|
70238
|
-
|
|
70963
|
+
padRight8("Pack", idWidth),
|
|
70964
|
+
padRight8("Source", sourceWidth),
|
|
70239
70965
|
"Ref"
|
|
70240
70966
|
].join(" ");
|
|
70241
70967
|
console.log(header);
|
|
70242
70968
|
console.log("-".repeat(header.length + 12));
|
|
70243
70969
|
for (const pack of lock.packs) {
|
|
70244
70970
|
console.log([
|
|
70245
|
-
|
|
70246
|
-
|
|
70971
|
+
padRight8(pack.id, idWidth),
|
|
70972
|
+
padRight8(pack.source, sourceWidth),
|
|
70247
70973
|
pack.ref.substring(0, 12)
|
|
70248
70974
|
].join(" "));
|
|
70249
70975
|
}
|
|
@@ -70253,8 +70979,8 @@ function printPacksStatus(repoRoot) {
|
|
|
70253
70979
|
console.log(` Agents: ${lock.effective.agents_count}`);
|
|
70254
70980
|
console.log(` Teams: ${lock.effective.teams_count}`);
|
|
70255
70981
|
console.log(` Routes: ${lock.effective.routes_count}`);
|
|
70256
|
-
if ((0,
|
|
70257
|
-
const manifestRaw = (0,
|
|
70982
|
+
if ((0, import_node_fs13.existsSync)(manifestPath)) {
|
|
70983
|
+
const manifestRaw = (0, import_node_fs13.readFileSync)(manifestPath, "utf-8");
|
|
70258
70984
|
const manifest = (0, import_yaml4.parse)(manifestRaw);
|
|
70259
70985
|
if (manifest) {
|
|
70260
70986
|
const xEve = manifest["x-eve"] ?? manifest["x_eve"] ?? {};
|
|
@@ -70299,12 +71025,12 @@ function detectDrift(lock, manifestPacks) {
|
|
|
70299
71025
|
}
|
|
70300
71026
|
function printPacksResolve(repoRoot, dryRun) {
|
|
70301
71027
|
if (dryRun) {
|
|
70302
|
-
const lockfilePath = (0,
|
|
70303
|
-
const manifestPath = (0,
|
|
70304
|
-
if (!(0,
|
|
71028
|
+
const lockfilePath = (0, import_node_path13.join)(repoRoot, ".eve", "packs.lock.yaml");
|
|
71029
|
+
const manifestPath = (0, import_node_path13.join)(repoRoot, ".eve", "manifest.yaml");
|
|
71030
|
+
if (!(0, import_node_fs13.existsSync)(manifestPath)) {
|
|
70305
71031
|
throw new Error("No manifest found at .eve/manifest.yaml");
|
|
70306
71032
|
}
|
|
70307
|
-
const manifestRaw = (0,
|
|
71033
|
+
const manifestRaw = (0, import_node_fs13.readFileSync)(manifestPath, "utf-8");
|
|
70308
71034
|
const manifest = (0, import_yaml4.parse)(manifestRaw);
|
|
70309
71035
|
if (!manifest) {
|
|
70310
71036
|
throw new Error("Manifest is empty or malformed.");
|
|
@@ -70322,8 +71048,8 @@ function printPacksResolve(repoRoot, dryRun) {
|
|
|
70322
71048
|
const refShort = mp.ref ? mp.ref.substring(0, 12) : "(local)";
|
|
70323
71049
|
console.log(` - ${mp.source} @ ${refShort}`);
|
|
70324
71050
|
}
|
|
70325
|
-
if ((0,
|
|
70326
|
-
const lockRaw = (0,
|
|
71051
|
+
if ((0, import_node_fs13.existsSync)(lockfilePath)) {
|
|
71052
|
+
const lockRaw = (0, import_node_fs13.readFileSync)(lockfilePath, "utf-8");
|
|
70327
71053
|
const lock = (0, import_yaml4.parse)(lockRaw);
|
|
70328
71054
|
if (lock?.packs) {
|
|
70329
71055
|
const drift = detectDrift(lock, manifestPacks);
|
|
@@ -70352,7 +71078,7 @@ function printPacksResolve(repoRoot, dryRun) {
|
|
|
70352
71078
|
console.log("");
|
|
70353
71079
|
console.log("This will resolve packs, merge configs, write the lockfile, and sync to the API.");
|
|
70354
71080
|
}
|
|
70355
|
-
function
|
|
71081
|
+
function padRight8(value, width) {
|
|
70356
71082
|
return value.length >= width ? value : `${value}${" ".repeat(width - value.length)}`;
|
|
70357
71083
|
}
|
|
70358
71084
|
|
|
@@ -71178,8 +71904,8 @@ Cursor: ${result.cursor}`);
|
|
|
71178
71904
|
}
|
|
71179
71905
|
|
|
71180
71906
|
// src/commands/migrate.ts
|
|
71181
|
-
var
|
|
71182
|
-
var
|
|
71907
|
+
var import_node_fs14 = require("node:fs");
|
|
71908
|
+
var import_node_path14 = require("node:path");
|
|
71183
71909
|
var import_yaml5 = require("yaml");
|
|
71184
71910
|
async function handleMigrate2(subcommand, _rest, _flags) {
|
|
71185
71911
|
switch (subcommand) {
|
|
@@ -71199,12 +71925,12 @@ async function migrateSkillsToPacks() {
|
|
|
71199
71925
|
if (!repoRoot) {
|
|
71200
71926
|
throw new Error("Not in a git repository. Run this from your project root.");
|
|
71201
71927
|
}
|
|
71202
|
-
const skillsTxtPath = (0,
|
|
71203
|
-
if (!(0,
|
|
71928
|
+
const skillsTxtPath = (0, import_node_path14.join)(repoRoot, "skills.txt");
|
|
71929
|
+
if (!(0, import_node_fs14.existsSync)(skillsTxtPath)) {
|
|
71204
71930
|
console.log("No skills.txt found at repository root. Nothing to migrate.");
|
|
71205
71931
|
return;
|
|
71206
71932
|
}
|
|
71207
|
-
const content = (0,
|
|
71933
|
+
const content = (0, import_node_fs14.readFileSync)(skillsTxtPath, "utf-8");
|
|
71208
71934
|
const lines = content.split("\n");
|
|
71209
71935
|
const localSources = [];
|
|
71210
71936
|
const remoteSources = [];
|
|
@@ -71313,9 +72039,9 @@ async function handleList8(flags, context2, json) {
|
|
|
71313
72039
|
const nameWidth = Math.max(12, ...response.managed.map((m) => m.display_name.length));
|
|
71314
72040
|
const providerWidth = Math.max(8, ...response.managed.map((m) => m.inference_provider.length));
|
|
71315
72041
|
const header = [
|
|
71316
|
-
|
|
71317
|
-
|
|
71318
|
-
|
|
72042
|
+
padRight9("Model Spec", specWidth),
|
|
72043
|
+
padRight9("Display Name", nameWidth),
|
|
72044
|
+
padRight9("Provider", providerWidth),
|
|
71319
72045
|
"Capabilities"
|
|
71320
72046
|
].join(" ");
|
|
71321
72047
|
console.log(header);
|
|
@@ -71326,9 +72052,9 @@ async function handleList8(flags, context2, json) {
|
|
|
71326
72052
|
if (model.capabilities.tool_calling) caps.push("tools");
|
|
71327
72053
|
if (model.capabilities.reasoning) caps.push("reason");
|
|
71328
72054
|
const row = [
|
|
71329
|
-
|
|
71330
|
-
|
|
71331
|
-
|
|
72055
|
+
padRight9(model.model_spec, specWidth),
|
|
72056
|
+
padRight9(model.display_name, nameWidth),
|
|
72057
|
+
padRight9(model.inference_provider, providerWidth),
|
|
71332
72058
|
caps.join(", ")
|
|
71333
72059
|
].join(" ");
|
|
71334
72060
|
console.log(row);
|
|
@@ -71338,16 +72064,95 @@ async function handleList8(flags, context2, json) {
|
|
|
71338
72064
|
console.log("");
|
|
71339
72065
|
console.log('Usage: Set harness_options.model to the model spec (e.g. "managed/deepseek-r1")');
|
|
71340
72066
|
}
|
|
71341
|
-
function
|
|
72067
|
+
function padRight9(str, width) {
|
|
71342
72068
|
if (str.length >= width) return str;
|
|
71343
72069
|
return str + " ".repeat(width - str.length);
|
|
71344
72070
|
}
|
|
71345
72071
|
|
|
71346
72072
|
// src/commands/access.ts
|
|
71347
|
-
var
|
|
71348
|
-
var
|
|
72073
|
+
var import_node_fs15 = require("node:fs");
|
|
72074
|
+
var import_node_path15 = require("node:path");
|
|
71349
72075
|
var readline3 = __toESM(require("node:readline/promises"));
|
|
71350
72076
|
var import_yaml6 = require("yaml");
|
|
72077
|
+
function resolvePrincipalSelection(flags, options) {
|
|
72078
|
+
const userId = getStringFlag(flags, ["user"]);
|
|
72079
|
+
const spId = getStringFlag(flags, ["service-principal", "sp"]);
|
|
72080
|
+
const groupId = options?.allowGroup ? getStringFlag(flags, ["group"]) : void 0;
|
|
72081
|
+
const selected = [
|
|
72082
|
+
userId ? "user" : null,
|
|
72083
|
+
spId ? "service_principal" : null,
|
|
72084
|
+
groupId ? "group" : null
|
|
72085
|
+
].filter(Boolean);
|
|
72086
|
+
if (selected.length === 0) {
|
|
72087
|
+
const groupHint = options?.allowGroup ? " or --group <group_id>" : "";
|
|
72088
|
+
throw new Error(`--user <user_id> or --service-principal <sp_id>${groupHint} is required`);
|
|
72089
|
+
}
|
|
72090
|
+
if (selected.length > 1) {
|
|
72091
|
+
throw new Error("Specify exactly one principal selector: --user, --service-principal, or --group");
|
|
72092
|
+
}
|
|
72093
|
+
if (userId) {
|
|
72094
|
+
return { principalType: "user", principalId: userId };
|
|
72095
|
+
}
|
|
72096
|
+
if (spId) {
|
|
72097
|
+
return { principalType: "service_principal", principalId: spId };
|
|
72098
|
+
}
|
|
72099
|
+
return { principalType: "group", principalId: groupId };
|
|
72100
|
+
}
|
|
72101
|
+
function parseScopeJsonFlag(flags) {
|
|
72102
|
+
const raw = getStringFlag(flags, ["scope-json"]);
|
|
72103
|
+
if (!raw) {
|
|
72104
|
+
return void 0;
|
|
72105
|
+
}
|
|
72106
|
+
try {
|
|
72107
|
+
const parsed = JSON.parse(raw);
|
|
72108
|
+
if (!parsed || typeof parsed !== "object" || Array.isArray(parsed)) {
|
|
72109
|
+
throw new Error("must be a JSON object");
|
|
72110
|
+
}
|
|
72111
|
+
return parsed;
|
|
72112
|
+
} catch (error) {
|
|
72113
|
+
const message = error instanceof Error ? error.message : String(error);
|
|
72114
|
+
throw new Error(`Invalid --scope-json: ${message}`);
|
|
72115
|
+
}
|
|
72116
|
+
}
|
|
72117
|
+
function normalizeBindingScope(scope) {
|
|
72118
|
+
if (!scope) {
|
|
72119
|
+
return void 0;
|
|
72120
|
+
}
|
|
72121
|
+
const normalized = {};
|
|
72122
|
+
if (scope.orgfs) {
|
|
72123
|
+
const allow = [...new Set(scope.orgfs.allow_prefixes ?? [])].sort();
|
|
72124
|
+
const readOnly = [...new Set(scope.orgfs.read_only_prefixes ?? [])].sort();
|
|
72125
|
+
if (allow.length > 0 || readOnly.length > 0) {
|
|
72126
|
+
normalized.orgfs = {};
|
|
72127
|
+
if (allow.length > 0) normalized.orgfs.allow_prefixes = allow;
|
|
72128
|
+
if (readOnly.length > 0) normalized.orgfs.read_only_prefixes = readOnly;
|
|
72129
|
+
}
|
|
72130
|
+
}
|
|
72131
|
+
if (scope.orgdocs) {
|
|
72132
|
+
const allow = [...new Set(scope.orgdocs.allow_prefixes ?? [])].sort();
|
|
72133
|
+
const readOnly = [...new Set(scope.orgdocs.read_only_prefixes ?? [])].sort();
|
|
72134
|
+
if (allow.length > 0 || readOnly.length > 0) {
|
|
72135
|
+
normalized.orgdocs = {};
|
|
72136
|
+
if (allow.length > 0) normalized.orgdocs.allow_prefixes = allow;
|
|
72137
|
+
if (readOnly.length > 0) normalized.orgdocs.read_only_prefixes = readOnly;
|
|
72138
|
+
}
|
|
72139
|
+
}
|
|
72140
|
+
if (scope.envdb) {
|
|
72141
|
+
const schemas = [...new Set(scope.envdb.schemas ?? [])].sort();
|
|
72142
|
+
const tables = [...new Set(scope.envdb.tables ?? [])].sort();
|
|
72143
|
+
if (schemas.length > 0 || tables.length > 0) {
|
|
72144
|
+
normalized.envdb = {};
|
|
72145
|
+
if (schemas.length > 0) normalized.envdb.schemas = schemas;
|
|
72146
|
+
if (tables.length > 0) normalized.envdb.tables = tables;
|
|
72147
|
+
}
|
|
72148
|
+
}
|
|
72149
|
+
return Object.keys(normalized).length > 0 ? normalized : void 0;
|
|
72150
|
+
}
|
|
72151
|
+
function scopeEquals(left, right) {
|
|
72152
|
+
const normalizedLeft = normalizeBindingScope(left);
|
|
72153
|
+
const normalizedRight = normalizeBindingScope(right);
|
|
72154
|
+
return JSON.stringify(normalizedLeft ?? null) === JSON.stringify(normalizedRight ?? null);
|
|
72155
|
+
}
|
|
71351
72156
|
async function handleAccess(subcommand, rest, flags, context2) {
|
|
71352
72157
|
const json = Boolean(flags.json);
|
|
71353
72158
|
switch (subcommand) {
|
|
@@ -71363,6 +72168,10 @@ async function handleAccess(subcommand, rest, flags, context2) {
|
|
|
71363
72168
|
return handleUnbind(flags, context2, json);
|
|
71364
72169
|
case "bindings":
|
|
71365
72170
|
return handleBindings(rest[0], flags, context2, json);
|
|
72171
|
+
case "groups":
|
|
72172
|
+
return handleGroups(rest[0], rest.slice(1), flags, context2, json);
|
|
72173
|
+
case "memberships":
|
|
72174
|
+
return handleMemberships(flags, context2, json);
|
|
71366
72175
|
case "validate":
|
|
71367
72176
|
return handleValidate(flags, json);
|
|
71368
72177
|
case "plan":
|
|
@@ -71371,38 +72180,44 @@ async function handleAccess(subcommand, rest, flags, context2) {
|
|
|
71371
72180
|
return handleSync(flags, context2, json);
|
|
71372
72181
|
default:
|
|
71373
72182
|
throw new Error(
|
|
71374
|
-
"Usage: eve access <subcommand> [flags]\n\nCommands:\n can Check if a principal can perform an action\n explain Explain permission resolution chain\n roles Manage custom access roles (create|list|show|update|delete)\n bind Bind a custom role to a principal\n unbind Remove a role binding from a principal\n bindings List access bindings\n validate Validate an .eve/access.yaml file\n plan Show changes needed to sync access.yaml to an org\n sync Apply access.yaml to an org (create/update/prune)"
|
|
72183
|
+
"Usage: eve access <subcommand> [flags]\n\nCommands:\n can Check if a principal can perform an action\n explain Explain permission resolution chain\n roles Manage custom access roles (create|list|show|update|delete)\n bind Bind a custom role to a principal\n unbind Remove a role binding from a principal\n bindings List access bindings\n groups Manage access groups and members\n memberships Inspect principal memberships and effective scopes\n validate Validate an .eve/access.yaml file\n plan Show changes needed to sync access.yaml to an org\n sync Apply access.yaml to an org (create/update/prune)"
|
|
71375
72184
|
);
|
|
71376
72185
|
}
|
|
71377
72186
|
}
|
|
71378
72187
|
async function handleCan(flags, context2, json) {
|
|
71379
72188
|
const orgId = getStringFlag(flags, ["org"]) ?? context2.orgId;
|
|
71380
|
-
const userId = getStringFlag(flags, ["user"]);
|
|
71381
|
-
const spId = getStringFlag(flags, ["service-principal", "sp"]);
|
|
71382
72189
|
const projectId = getStringFlag(flags, ["project"]) ?? context2.projectId;
|
|
71383
72190
|
const permission = getStringFlag(flags, ["permission"]);
|
|
72191
|
+
const resourceType = getStringFlag(flags, ["resource-type"]);
|
|
72192
|
+
const resourceId = getStringFlag(flags, ["resource", "resource-id"]);
|
|
72193
|
+
const action = getStringFlag(flags, ["action"]);
|
|
71384
72194
|
if (!orgId) {
|
|
71385
72195
|
throw new Error("--org is required (or set a default org in your profile)");
|
|
71386
72196
|
}
|
|
71387
72197
|
if (!permission) {
|
|
71388
72198
|
throw new Error("--permission is required");
|
|
71389
72199
|
}
|
|
71390
|
-
|
|
71391
|
-
|
|
71392
|
-
|
|
71393
|
-
if (userId && spId) {
|
|
71394
|
-
throw new Error("Specify either --user or --service-principal, not both");
|
|
72200
|
+
const principal = resolvePrincipalSelection(flags, { allowGroup: true });
|
|
72201
|
+
if ((resourceType || action) && !resourceId) {
|
|
72202
|
+
throw new Error("--resource is required when --resource-type or --action is provided");
|
|
71395
72203
|
}
|
|
71396
|
-
const principalType = userId ? "user" : "service_principal";
|
|
71397
|
-
const principalId = userId ?? spId;
|
|
71398
72204
|
const params = new URLSearchParams({
|
|
71399
|
-
principal_type: principalType,
|
|
71400
|
-
principal_id: principalId,
|
|
72205
|
+
principal_type: principal.principalType,
|
|
72206
|
+
principal_id: principal.principalId,
|
|
71401
72207
|
permission
|
|
71402
72208
|
});
|
|
71403
72209
|
if (projectId) {
|
|
71404
72210
|
params.set("project_id", projectId);
|
|
71405
72211
|
}
|
|
72212
|
+
if (resourceType) {
|
|
72213
|
+
params.set("resource_type", resourceType);
|
|
72214
|
+
}
|
|
72215
|
+
if (resourceId) {
|
|
72216
|
+
params.set("resource_id", resourceId);
|
|
72217
|
+
}
|
|
72218
|
+
if (action) {
|
|
72219
|
+
params.set("action", action);
|
|
72220
|
+
}
|
|
71406
72221
|
const response = await requestJson(
|
|
71407
72222
|
context2,
|
|
71408
72223
|
`/orgs/${orgId}/access/can?${params.toString()}`
|
|
@@ -71413,35 +72228,47 @@ async function handleCan(flags, context2, json) {
|
|
|
71413
72228
|
}
|
|
71414
72229
|
const label = response.allowed ? "ALLOWED" : "DENIED";
|
|
71415
72230
|
console.log(`${label} (source: ${response.source})`);
|
|
72231
|
+
if (response.resource) {
|
|
72232
|
+
const resourceStatus = response.resource.scope_matched ? "scope match" : "scope denied";
|
|
72233
|
+
console.log(
|
|
72234
|
+
`Resource: ${response.resource.type}:${response.resource.id} [${response.resource.action}] (${resourceStatus})`
|
|
72235
|
+
);
|
|
72236
|
+
}
|
|
71416
72237
|
}
|
|
71417
72238
|
async function handleExplain(flags, context2, json) {
|
|
71418
72239
|
const orgId = getStringFlag(flags, ["org"]) ?? context2.orgId;
|
|
71419
|
-
const userId = getStringFlag(flags, ["user"]);
|
|
71420
|
-
const spId = getStringFlag(flags, ["service-principal", "sp"]);
|
|
71421
72240
|
const projectId = getStringFlag(flags, ["project"]) ?? context2.projectId;
|
|
71422
72241
|
const permission = getStringFlag(flags, ["permission"]);
|
|
72242
|
+
const resourceType = getStringFlag(flags, ["resource-type"]);
|
|
72243
|
+
const resourceId = getStringFlag(flags, ["resource", "resource-id"]);
|
|
72244
|
+
const action = getStringFlag(flags, ["action"]);
|
|
71423
72245
|
if (!orgId) {
|
|
71424
72246
|
throw new Error("--org is required (or set a default org in your profile)");
|
|
71425
72247
|
}
|
|
71426
72248
|
if (!permission) {
|
|
71427
72249
|
throw new Error("--permission is required");
|
|
71428
72250
|
}
|
|
71429
|
-
|
|
71430
|
-
|
|
71431
|
-
|
|
71432
|
-
if (userId && spId) {
|
|
71433
|
-
throw new Error("Specify either --user or --service-principal, not both");
|
|
72251
|
+
const principal = resolvePrincipalSelection(flags, { allowGroup: true });
|
|
72252
|
+
if ((resourceType || action) && !resourceId) {
|
|
72253
|
+
throw new Error("--resource is required when --resource-type or --action is provided");
|
|
71434
72254
|
}
|
|
71435
|
-
const principalType = userId ? "user" : "service_principal";
|
|
71436
|
-
const principalId = userId ?? spId;
|
|
71437
72255
|
const params = new URLSearchParams({
|
|
71438
|
-
principal_type: principalType,
|
|
71439
|
-
principal_id: principalId,
|
|
72256
|
+
principal_type: principal.principalType,
|
|
72257
|
+
principal_id: principal.principalId,
|
|
71440
72258
|
permission
|
|
71441
72259
|
});
|
|
71442
72260
|
if (projectId) {
|
|
71443
72261
|
params.set("project_id", projectId);
|
|
71444
72262
|
}
|
|
72263
|
+
if (resourceType) {
|
|
72264
|
+
params.set("resource_type", resourceType);
|
|
72265
|
+
}
|
|
72266
|
+
if (resourceId) {
|
|
72267
|
+
params.set("resource_id", resourceId);
|
|
72268
|
+
}
|
|
72269
|
+
if (action) {
|
|
72270
|
+
params.set("action", action);
|
|
72271
|
+
}
|
|
71445
72272
|
const response = await requestJson(
|
|
71446
72273
|
context2,
|
|
71447
72274
|
`/orgs/${orgId}/access/explain?${params.toString()}`
|
|
@@ -71452,13 +72279,20 @@ async function handleExplain(flags, context2, json) {
|
|
|
71452
72279
|
}
|
|
71453
72280
|
console.log(`Permission: ${response.permission}`);
|
|
71454
72281
|
console.log(`Result: ${response.result}`);
|
|
72282
|
+
if (response.resource) {
|
|
72283
|
+
const resourceStatus = response.resource.scope_matched ? "scope match" : "scope denied";
|
|
72284
|
+
console.log(
|
|
72285
|
+
`Resource: ${response.resource.type}:${response.resource.id} [${response.resource.action}] (${resourceStatus})`
|
|
72286
|
+
);
|
|
72287
|
+
}
|
|
71455
72288
|
if (response.grants.length > 0) {
|
|
71456
72289
|
console.log("Grants found:");
|
|
71457
72290
|
for (const grant of response.grants) {
|
|
71458
72291
|
const roleLabel = grant.role ? `: ${grant.role}` : "";
|
|
71459
72292
|
const permCount = grant.permissions.length;
|
|
71460
72293
|
const status = grant.has_permission ? "has permission" : `missing ${response.permission}`;
|
|
71461
|
-
|
|
72294
|
+
const scopeSuffix = grant.scope_match === void 0 ? "" : grant.scope_match ? " [scope:match]" : ` [scope:deny${grant.scope_reason ? `: ${grant.scope_reason}` : ""}]`;
|
|
72295
|
+
console.log(` - ${grant.source}${roleLabel} -> [${permCount} permissions] (${status})${scopeSuffix}`);
|
|
71462
72296
|
}
|
|
71463
72297
|
} else {
|
|
71464
72298
|
console.log("Grants found: none");
|
|
@@ -71638,32 +72472,27 @@ async function handleRoles(action, positionals, flags, context2, json) {
|
|
|
71638
72472
|
}
|
|
71639
72473
|
async function handleBind(flags, context2, json) {
|
|
71640
72474
|
const orgId = getStringFlag(flags, ["org"]) ?? context2.orgId;
|
|
71641
|
-
const userId = getStringFlag(flags, ["user"]);
|
|
71642
|
-
const spId = getStringFlag(flags, ["service-principal", "sp"]);
|
|
71643
72475
|
const projectId = getStringFlag(flags, ["project"]) ?? context2.projectId;
|
|
71644
72476
|
const roleName = getStringFlag(flags, ["role"]);
|
|
72477
|
+
const scopeJson = parseScopeJsonFlag(flags);
|
|
71645
72478
|
if (!orgId) {
|
|
71646
72479
|
throw new Error("--org is required (or set a default org in your profile)");
|
|
71647
72480
|
}
|
|
71648
72481
|
if (!roleName) {
|
|
71649
72482
|
throw new Error("--role <role_name> is required");
|
|
71650
72483
|
}
|
|
71651
|
-
|
|
71652
|
-
throw new Error("--user <user_id> or --service-principal <sp_id> is required");
|
|
71653
|
-
}
|
|
71654
|
-
if (userId && spId) {
|
|
71655
|
-
throw new Error("Specify either --user or --service-principal, not both");
|
|
71656
|
-
}
|
|
71657
|
-
const principalType = userId ? "user" : "service_principal";
|
|
71658
|
-
const principalId = userId ?? spId;
|
|
72484
|
+
const principal = resolvePrincipalSelection(flags, { allowGroup: true });
|
|
71659
72485
|
const body = {
|
|
71660
72486
|
role_name: roleName,
|
|
71661
|
-
principal_type: principalType,
|
|
71662
|
-
principal_id: principalId
|
|
72487
|
+
principal_type: principal.principalType,
|
|
72488
|
+
principal_id: principal.principalId
|
|
71663
72489
|
};
|
|
71664
72490
|
if (projectId) {
|
|
71665
72491
|
body.project_id = projectId;
|
|
71666
72492
|
}
|
|
72493
|
+
if (scopeJson) {
|
|
72494
|
+
body.scope_json = scopeJson;
|
|
72495
|
+
}
|
|
71667
72496
|
const response = await requestJson(
|
|
71668
72497
|
context2,
|
|
71669
72498
|
`/orgs/${orgId}/access/bindings`,
|
|
@@ -71674,12 +72503,10 @@ async function handleBind(flags, context2, json) {
|
|
|
71674
72503
|
return;
|
|
71675
72504
|
}
|
|
71676
72505
|
const scopeLabel = response.project_id ? `project ${response.project_id}` : `org ${orgId}`;
|
|
71677
|
-
console.log(`Bound role '${response.role_name}' to ${principalType} ${principalId} on ${scopeLabel}`);
|
|
72506
|
+
console.log(`Bound role '${response.role_name}' to ${principal.principalType} ${principal.principalId} on ${scopeLabel}`);
|
|
71678
72507
|
}
|
|
71679
72508
|
async function handleUnbind(flags, context2, json) {
|
|
71680
72509
|
const orgId = getStringFlag(flags, ["org"]) ?? context2.orgId;
|
|
71681
|
-
const userId = getStringFlag(flags, ["user"]);
|
|
71682
|
-
const spId = getStringFlag(flags, ["service-principal", "sp"]);
|
|
71683
72510
|
const projectId = getStringFlag(flags, ["project"]) ?? context2.projectId;
|
|
71684
72511
|
const roleName = getStringFlag(flags, ["role"]);
|
|
71685
72512
|
if (!orgId) {
|
|
@@ -71688,17 +72515,10 @@ async function handleUnbind(flags, context2, json) {
|
|
|
71688
72515
|
if (!roleName) {
|
|
71689
72516
|
throw new Error("--role <role_name> is required");
|
|
71690
72517
|
}
|
|
71691
|
-
|
|
71692
|
-
throw new Error("--user <user_id> or --service-principal <sp_id> is required");
|
|
71693
|
-
}
|
|
71694
|
-
if (userId && spId) {
|
|
71695
|
-
throw new Error("Specify either --user or --service-principal, not both");
|
|
71696
|
-
}
|
|
71697
|
-
const principalType = userId ? "user" : "service_principal";
|
|
71698
|
-
const principalId = userId ?? spId;
|
|
72518
|
+
const principal = resolvePrincipalSelection(flags, { allowGroup: true });
|
|
71699
72519
|
const params = new URLSearchParams({
|
|
71700
|
-
principal_type: principalType,
|
|
71701
|
-
principal_id: principalId
|
|
72520
|
+
principal_type: principal.principalType,
|
|
72521
|
+
principal_id: principal.principalId
|
|
71702
72522
|
});
|
|
71703
72523
|
if (projectId) {
|
|
71704
72524
|
params.set("project_id", projectId);
|
|
@@ -71710,14 +72530,14 @@ async function handleUnbind(flags, context2, json) {
|
|
|
71710
72530
|
const bindings = unwrapListResponse(bindingsResponse);
|
|
71711
72531
|
const matching = bindings.find((b) => {
|
|
71712
72532
|
if (b.role_name !== roleName) return false;
|
|
71713
|
-
if (b.principal_type !== principalType) return false;
|
|
71714
|
-
if (b.principal_id !== principalId) return false;
|
|
72533
|
+
if (b.principal_type !== principal.principalType) return false;
|
|
72534
|
+
if (b.principal_id !== principal.principalId) return false;
|
|
71715
72535
|
if (projectId) return b.project_id === projectId;
|
|
71716
72536
|
return b.project_id === null;
|
|
71717
72537
|
});
|
|
71718
72538
|
if (!matching) {
|
|
71719
72539
|
throw new Error(
|
|
71720
|
-
`No binding found for role '${roleName}' on ${principalType} ${principalId}` + (projectId ? ` in project ${projectId}` : " at org level")
|
|
72540
|
+
`No binding found for role '${roleName}' on ${principal.principalType} ${principal.principalId}` + (projectId ? ` in project ${projectId}` : " at org level")
|
|
71721
72541
|
);
|
|
71722
72542
|
}
|
|
71723
72543
|
await requestJson(
|
|
@@ -71730,7 +72550,7 @@ async function handleUnbind(flags, context2, json) {
|
|
|
71730
72550
|
return;
|
|
71731
72551
|
}
|
|
71732
72552
|
const scopeLabel = projectId ? `project ${projectId}` : `org ${orgId}`;
|
|
71733
|
-
console.log(`Unbound role '${roleName}' from ${principalType} ${principalId} on ${scopeLabel}`);
|
|
72553
|
+
console.log(`Unbound role '${roleName}' from ${principal.principalType} ${principal.principalId} on ${scopeLabel}`);
|
|
71734
72554
|
}
|
|
71735
72555
|
async function handleBindings(action, flags, context2, json) {
|
|
71736
72556
|
if (action && action !== "list") {
|
|
@@ -71762,16 +72582,262 @@ async function handleBindings(action, flags, context2, json) {
|
|
|
71762
72582
|
console.log(`${b.role_name} -> ${b.principal_type} ${b.principal_id} (${scope})`);
|
|
71763
72583
|
}
|
|
71764
72584
|
}
|
|
72585
|
+
async function handleGroups(action, positionals, flags, context2, json) {
|
|
72586
|
+
const orgId = getStringFlag(flags, ["org"]) ?? context2.orgId;
|
|
72587
|
+
if (!orgId) {
|
|
72588
|
+
throw new Error("--org is required (or set a default org in your profile)");
|
|
72589
|
+
}
|
|
72590
|
+
if (action === "members") {
|
|
72591
|
+
return handleGroupMembers(positionals[0], positionals.slice(1), flags, context2, json);
|
|
72592
|
+
}
|
|
72593
|
+
switch (action) {
|
|
72594
|
+
case "create": {
|
|
72595
|
+
const name = positionals[0] ?? getStringFlag(flags, ["name"]);
|
|
72596
|
+
const slug = getStringFlag(flags, ["slug"]);
|
|
72597
|
+
const description = getStringFlag(flags, ["description"]);
|
|
72598
|
+
if (!name) {
|
|
72599
|
+
throw new Error("Usage: eve access groups create <name> --org <org_id> [--slug <slug>] [--description <text>]");
|
|
72600
|
+
}
|
|
72601
|
+
const response = await requestJson(context2, `/orgs/${orgId}/access/groups`, {
|
|
72602
|
+
method: "POST",
|
|
72603
|
+
body: { name, slug, description }
|
|
72604
|
+
});
|
|
72605
|
+
if (json) {
|
|
72606
|
+
outputJson(response, json);
|
|
72607
|
+
return;
|
|
72608
|
+
}
|
|
72609
|
+
console.log(`Created group '${response.name}' (${response.id})`);
|
|
72610
|
+
console.log(` Slug: ${response.slug}`);
|
|
72611
|
+
if (response.description) {
|
|
72612
|
+
console.log(` Description: ${response.description}`);
|
|
72613
|
+
}
|
|
72614
|
+
return;
|
|
72615
|
+
}
|
|
72616
|
+
case "list": {
|
|
72617
|
+
const listResponse = await requestJson(
|
|
72618
|
+
context2,
|
|
72619
|
+
`/orgs/${orgId}/access/groups`
|
|
72620
|
+
);
|
|
72621
|
+
const groups = unwrapListResponse(listResponse);
|
|
72622
|
+
if (json) {
|
|
72623
|
+
outputJson({ data: groups }, json);
|
|
72624
|
+
return;
|
|
72625
|
+
}
|
|
72626
|
+
if (groups.length === 0) {
|
|
72627
|
+
console.log("No access groups found.");
|
|
72628
|
+
return;
|
|
72629
|
+
}
|
|
72630
|
+
for (const group of groups) {
|
|
72631
|
+
const desc = group.description ? ` - ${group.description}` : "";
|
|
72632
|
+
console.log(`${group.slug} (${group.id})${desc}`);
|
|
72633
|
+
}
|
|
72634
|
+
return;
|
|
72635
|
+
}
|
|
72636
|
+
case "show": {
|
|
72637
|
+
const group = positionals[0] ?? getStringFlag(flags, ["group"]);
|
|
72638
|
+
if (!group) {
|
|
72639
|
+
throw new Error("Usage: eve access groups show <group_id_or_slug> --org <org_id>");
|
|
72640
|
+
}
|
|
72641
|
+
const response = await requestJson(
|
|
72642
|
+
context2,
|
|
72643
|
+
`/orgs/${orgId}/access/groups/${encodeURIComponent(group)}`
|
|
72644
|
+
);
|
|
72645
|
+
if (json) {
|
|
72646
|
+
outputJson(response, json);
|
|
72647
|
+
return;
|
|
72648
|
+
}
|
|
72649
|
+
console.log(`Name: ${response.name}`);
|
|
72650
|
+
console.log(`ID: ${response.id}`);
|
|
72651
|
+
console.log(`Slug: ${response.slug}`);
|
|
72652
|
+
if (response.description) {
|
|
72653
|
+
console.log(`Description: ${response.description}`);
|
|
72654
|
+
}
|
|
72655
|
+
console.log(`Created: ${response.created_at}`);
|
|
72656
|
+
console.log(`Updated: ${response.updated_at}`);
|
|
72657
|
+
return;
|
|
72658
|
+
}
|
|
72659
|
+
case "update": {
|
|
72660
|
+
const group = positionals[0] ?? getStringFlag(flags, ["group"]);
|
|
72661
|
+
if (!group) {
|
|
72662
|
+
throw new Error("Usage: eve access groups update <group_id_or_slug> --org <org_id> [--name <name>] [--slug <slug>] [--description <text>]");
|
|
72663
|
+
}
|
|
72664
|
+
const name = getStringFlag(flags, ["name"]);
|
|
72665
|
+
const slug = getStringFlag(flags, ["slug"]);
|
|
72666
|
+
const description = getStringFlag(flags, ["description"]);
|
|
72667
|
+
if (name === void 0 && slug === void 0 && description === void 0) {
|
|
72668
|
+
throw new Error("Nothing to update. Use --name, --slug, or --description");
|
|
72669
|
+
}
|
|
72670
|
+
const response = await requestJson(
|
|
72671
|
+
context2,
|
|
72672
|
+
`/orgs/${orgId}/access/groups/${encodeURIComponent(group)}`,
|
|
72673
|
+
{
|
|
72674
|
+
method: "PATCH",
|
|
72675
|
+
body: {
|
|
72676
|
+
name,
|
|
72677
|
+
slug,
|
|
72678
|
+
description
|
|
72679
|
+
}
|
|
72680
|
+
}
|
|
72681
|
+
);
|
|
72682
|
+
if (json) {
|
|
72683
|
+
outputJson(response, json);
|
|
72684
|
+
return;
|
|
72685
|
+
}
|
|
72686
|
+
console.log(`Updated group '${response.name}' (${response.id})`);
|
|
72687
|
+
console.log(` Slug: ${response.slug}`);
|
|
72688
|
+
if (response.description) {
|
|
72689
|
+
console.log(` Description: ${response.description}`);
|
|
72690
|
+
}
|
|
72691
|
+
return;
|
|
72692
|
+
}
|
|
72693
|
+
case "delete": {
|
|
72694
|
+
const group = positionals[0] ?? getStringFlag(flags, ["group"]);
|
|
72695
|
+
if (!group) {
|
|
72696
|
+
throw new Error("Usage: eve access groups delete <group_id_or_slug> --org <org_id>");
|
|
72697
|
+
}
|
|
72698
|
+
await requestJson(context2, `/orgs/${orgId}/access/groups/${encodeURIComponent(group)}`, {
|
|
72699
|
+
method: "DELETE"
|
|
72700
|
+
});
|
|
72701
|
+
if (json) {
|
|
72702
|
+
outputJson({ deleted: true, group }, json);
|
|
72703
|
+
return;
|
|
72704
|
+
}
|
|
72705
|
+
console.log(`Deleted group '${group}'`);
|
|
72706
|
+
return;
|
|
72707
|
+
}
|
|
72708
|
+
default:
|
|
72709
|
+
throw new Error(
|
|
72710
|
+
'Usage: eve access groups <create|list|show|update|delete|members> [args] [flags]\n\nExamples:\n eve access groups create "Product Management" --org org_xxx\n eve access groups list --org org_xxx\n eve access groups members list pm-team --org org_xxx'
|
|
72711
|
+
);
|
|
72712
|
+
}
|
|
72713
|
+
}
|
|
72714
|
+
async function handleGroupMembers(action, positionals, flags, context2, json) {
|
|
72715
|
+
const orgId = getStringFlag(flags, ["org"]) ?? context2.orgId;
|
|
72716
|
+
if (!orgId) {
|
|
72717
|
+
throw new Error("--org is required (or set a default org in your profile)");
|
|
72718
|
+
}
|
|
72719
|
+
switch (action) {
|
|
72720
|
+
case "add": {
|
|
72721
|
+
const group = positionals[0] ?? getStringFlag(flags, ["group"]);
|
|
72722
|
+
if (!group) {
|
|
72723
|
+
throw new Error("Usage: eve access groups members add <group> --org <org_id> (--user <user_id> | --service-principal <sp_id>)");
|
|
72724
|
+
}
|
|
72725
|
+
const principal = resolvePrincipalSelection(flags);
|
|
72726
|
+
const response = await requestJson(
|
|
72727
|
+
context2,
|
|
72728
|
+
`/orgs/${orgId}/access/groups/${encodeURIComponent(group)}/members`,
|
|
72729
|
+
{
|
|
72730
|
+
method: "POST",
|
|
72731
|
+
body: {
|
|
72732
|
+
principal_type: principal.principalType,
|
|
72733
|
+
principal_id: principal.principalId
|
|
72734
|
+
}
|
|
72735
|
+
}
|
|
72736
|
+
);
|
|
72737
|
+
if (json) {
|
|
72738
|
+
outputJson(response, json);
|
|
72739
|
+
return;
|
|
72740
|
+
}
|
|
72741
|
+
console.log(`Added ${response.principal_type} ${response.principal_id} to group ${group}`);
|
|
72742
|
+
return;
|
|
72743
|
+
}
|
|
72744
|
+
case "list": {
|
|
72745
|
+
const group = positionals[0] ?? getStringFlag(flags, ["group"]);
|
|
72746
|
+
if (!group) {
|
|
72747
|
+
throw new Error("Usage: eve access groups members list <group> --org <org_id>");
|
|
72748
|
+
}
|
|
72749
|
+
const membersResponse = await requestJson(
|
|
72750
|
+
context2,
|
|
72751
|
+
`/orgs/${orgId}/access/groups/${encodeURIComponent(group)}/members`
|
|
72752
|
+
);
|
|
72753
|
+
const members = unwrapListResponse(membersResponse);
|
|
72754
|
+
if (json) {
|
|
72755
|
+
outputJson({ data: members }, json);
|
|
72756
|
+
return;
|
|
72757
|
+
}
|
|
72758
|
+
if (members.length === 0) {
|
|
72759
|
+
console.log("No group members found.");
|
|
72760
|
+
return;
|
|
72761
|
+
}
|
|
72762
|
+
for (const member of members) {
|
|
72763
|
+
console.log(`${member.principal_type} ${member.principal_id}`);
|
|
72764
|
+
}
|
|
72765
|
+
return;
|
|
72766
|
+
}
|
|
72767
|
+
case "remove": {
|
|
72768
|
+
const group = positionals[0] ?? getStringFlag(flags, ["group"]);
|
|
72769
|
+
if (!group) {
|
|
72770
|
+
throw new Error("Usage: eve access groups members remove <group> --org <org_id> (--user <user_id> | --service-principal <sp_id>)");
|
|
72771
|
+
}
|
|
72772
|
+
const principal = resolvePrincipalSelection(flags);
|
|
72773
|
+
await requestJson(
|
|
72774
|
+
context2,
|
|
72775
|
+
`/orgs/${orgId}/access/groups/${encodeURIComponent(group)}/members/${principal.principalType}/${principal.principalId}`,
|
|
72776
|
+
{ method: "DELETE" }
|
|
72777
|
+
);
|
|
72778
|
+
if (json) {
|
|
72779
|
+
outputJson({ removed: true, group, principal: principal.principalId }, json);
|
|
72780
|
+
return;
|
|
72781
|
+
}
|
|
72782
|
+
console.log(`Removed ${principal.principalType} ${principal.principalId} from group ${group}`);
|
|
72783
|
+
return;
|
|
72784
|
+
}
|
|
72785
|
+
default:
|
|
72786
|
+
throw new Error(
|
|
72787
|
+
"Usage: eve access groups members <add|list|remove> <group> --org <org_id> [--user <id> | --service-principal <id>]"
|
|
72788
|
+
);
|
|
72789
|
+
}
|
|
72790
|
+
}
|
|
72791
|
+
async function handleMemberships(flags, context2, json) {
|
|
72792
|
+
const orgId = getStringFlag(flags, ["org"]) ?? context2.orgId;
|
|
72793
|
+
if (!orgId) {
|
|
72794
|
+
throw new Error("--org is required (or set a default org in your profile)");
|
|
72795
|
+
}
|
|
72796
|
+
const principal = resolvePrincipalSelection(flags, { allowGroup: true });
|
|
72797
|
+
const response = await requestJson(
|
|
72798
|
+
context2,
|
|
72799
|
+
`/orgs/${orgId}/access/principals/${principal.principalType}/${principal.principalId}/memberships`
|
|
72800
|
+
);
|
|
72801
|
+
if (json) {
|
|
72802
|
+
outputJson(response, json);
|
|
72803
|
+
return;
|
|
72804
|
+
}
|
|
72805
|
+
console.log(`Principal: ${response.principal_type} ${response.principal_id}`);
|
|
72806
|
+
if (response.base.org_role) {
|
|
72807
|
+
console.log(`Org role: ${response.base.org_role}`);
|
|
72808
|
+
}
|
|
72809
|
+
if (response.base.project_roles.length > 0) {
|
|
72810
|
+
console.log(`Project roles: ${response.base.project_roles.length}`);
|
|
72811
|
+
}
|
|
72812
|
+
if (response.groups.length > 0) {
|
|
72813
|
+
console.log(`Groups: ${response.groups.map((group) => group.slug).join(", ")}`);
|
|
72814
|
+
} else {
|
|
72815
|
+
console.log("Groups: none");
|
|
72816
|
+
}
|
|
72817
|
+
console.log(`Effective permissions: ${response.effective_permissions.length}`);
|
|
72818
|
+
if (response.effective_scopes.orgfs.allow_prefixes.length > 0) {
|
|
72819
|
+
console.log(`orgfs allow: ${response.effective_scopes.orgfs.allow_prefixes.join(", ")}`);
|
|
72820
|
+
}
|
|
72821
|
+
if (response.effective_scopes.orgdocs.allow_prefixes.length > 0) {
|
|
72822
|
+
console.log(`orgdocs allow: ${response.effective_scopes.orgdocs.allow_prefixes.join(", ")}`);
|
|
72823
|
+
}
|
|
72824
|
+
if (response.effective_scopes.envdb.schemas.length > 0 || response.effective_scopes.envdb.tables.length > 0) {
|
|
72825
|
+
const schemas = response.effective_scopes.envdb.schemas.join(", ") || "-";
|
|
72826
|
+
const tables = response.effective_scopes.envdb.tables.join(", ") || "-";
|
|
72827
|
+
console.log(`envdb schemas: ${schemas}`);
|
|
72828
|
+
console.log(`envdb tables: ${tables}`);
|
|
72829
|
+
}
|
|
72830
|
+
}
|
|
71765
72831
|
var DEFAULT_ACCESS_YAML = ".eve/access.yaml";
|
|
71766
72832
|
function resolveFilePath(flags) {
|
|
71767
72833
|
const filePath = getStringFlag(flags, ["file", "f"]) ?? DEFAULT_ACCESS_YAML;
|
|
71768
|
-
return (0,
|
|
72834
|
+
return (0, import_node_path15.resolve)(process.cwd(), filePath);
|
|
71769
72835
|
}
|
|
71770
72836
|
function loadAccessYaml(filePath) {
|
|
71771
|
-
if (!(0,
|
|
72837
|
+
if (!(0, import_node_fs15.existsSync)(filePath)) {
|
|
71772
72838
|
throw new Error(`File not found: ${filePath}`);
|
|
71773
72839
|
}
|
|
71774
|
-
const raw = (0,
|
|
72840
|
+
const raw = (0, import_node_fs15.readFileSync)(filePath, "utf-8");
|
|
71775
72841
|
let parsed;
|
|
71776
72842
|
try {
|
|
71777
72843
|
parsed = (0, import_yaml6.parse)(raw);
|
|
@@ -71789,10 +72855,62 @@ ${issues.join("\n")}`);
|
|
|
71789
72855
|
}
|
|
71790
72856
|
return result.data;
|
|
71791
72857
|
}
|
|
72858
|
+
function permissionScopeType(permission) {
|
|
72859
|
+
if (permission.startsWith("orgfs:")) return "orgfs";
|
|
72860
|
+
if (permission.startsWith("orgdocs:")) return "orgdocs";
|
|
72861
|
+
if (permission.startsWith("envdb:")) return "envdb";
|
|
72862
|
+
return null;
|
|
72863
|
+
}
|
|
72864
|
+
function hasPrefixScope(scope, resourceType, requireWritable) {
|
|
72865
|
+
const resourceScope = scope?.[resourceType];
|
|
72866
|
+
if (!resourceScope) {
|
|
72867
|
+
return false;
|
|
72868
|
+
}
|
|
72869
|
+
const allowCount = resourceScope.allow_prefixes?.length ?? 0;
|
|
72870
|
+
const readOnlyCount = resourceScope.read_only_prefixes?.length ?? 0;
|
|
72871
|
+
if (requireWritable) {
|
|
72872
|
+
return allowCount > 0;
|
|
72873
|
+
}
|
|
72874
|
+
return allowCount > 0 || readOnlyCount > 0;
|
|
72875
|
+
}
|
|
72876
|
+
function hasEnvDbScope(scope) {
|
|
72877
|
+
const envdb = scope?.envdb;
|
|
72878
|
+
if (!envdb) {
|
|
72879
|
+
return false;
|
|
72880
|
+
}
|
|
72881
|
+
return (envdb.schemas?.length ?? 0) > 0 || (envdb.tables?.length ?? 0) > 0;
|
|
72882
|
+
}
|
|
72883
|
+
function groupDefaultName(slug) {
|
|
72884
|
+
return slug.split("-").filter(Boolean).map((part) => part.charAt(0).toUpperCase() + part.slice(1)).join(" ");
|
|
72885
|
+
}
|
|
72886
|
+
function declaredMemberKey(member) {
|
|
72887
|
+
return `${member.type}:${member.id}`;
|
|
72888
|
+
}
|
|
72889
|
+
function apiMemberKey(member) {
|
|
72890
|
+
return `${member.principal_type}:${member.principal_id}`;
|
|
72891
|
+
}
|
|
72892
|
+
function bindingIdentityKey(roleName, principalType, principalId, projectId) {
|
|
72893
|
+
const scope = projectId ?? "org";
|
|
72894
|
+
return `${roleName}|${principalType}|${principalId}|${scope}`;
|
|
72895
|
+
}
|
|
72896
|
+
function bindingMatchKey(roleName, principalType, principalId, projectId, scope) {
|
|
72897
|
+
const normalizedScope = normalizeBindingScope(scope);
|
|
72898
|
+
return `${bindingIdentityKey(roleName, principalType, principalId, projectId)}|${JSON.stringify(normalizedScope ?? null)}`;
|
|
72899
|
+
}
|
|
72900
|
+
function formatBindingScope(scope) {
|
|
72901
|
+
const normalized = normalizeBindingScope(scope);
|
|
72902
|
+
if (!normalized) {
|
|
72903
|
+
return "(none)";
|
|
72904
|
+
}
|
|
72905
|
+
return JSON.stringify(normalized);
|
|
72906
|
+
}
|
|
71792
72907
|
function semanticValidate(yaml) {
|
|
71793
72908
|
const errors = [];
|
|
71794
|
-
const
|
|
71795
|
-
|
|
72909
|
+
const roles = yaml.access.roles ?? {};
|
|
72910
|
+
const groups = yaml.access.groups ?? {};
|
|
72911
|
+
const roleNames = new Set(Object.keys(roles));
|
|
72912
|
+
const groupSlugs = new Set(Object.keys(groups));
|
|
72913
|
+
for (const [name, role] of Object.entries(roles)) {
|
|
71796
72914
|
for (const perm of role.permissions) {
|
|
71797
72915
|
if (!PERMISSION_SET.has(perm)) {
|
|
71798
72916
|
errors.push({
|
|
@@ -71802,6 +72920,23 @@ function semanticValidate(yaml) {
|
|
|
71802
72920
|
}
|
|
71803
72921
|
}
|
|
71804
72922
|
}
|
|
72923
|
+
for (const [slug, group] of Object.entries(groups)) {
|
|
72924
|
+
const seenMembers = /* @__PURE__ */ new Set();
|
|
72925
|
+
const members = group.members ?? [];
|
|
72926
|
+
for (let i = 0; i < members.length; i++) {
|
|
72927
|
+
const member = members[i];
|
|
72928
|
+
const key = declaredMemberKey(member);
|
|
72929
|
+
if (seenMembers.has(key)) {
|
|
72930
|
+
errors.push({
|
|
72931
|
+
path: `access.groups.${slug}.members[${i}]`,
|
|
72932
|
+
message: `Duplicate group member '${key}'`
|
|
72933
|
+
});
|
|
72934
|
+
continue;
|
|
72935
|
+
}
|
|
72936
|
+
seenMembers.add(key);
|
|
72937
|
+
}
|
|
72938
|
+
}
|
|
72939
|
+
const seenBindingKeys = /* @__PURE__ */ new Set();
|
|
71805
72940
|
const bindings = yaml.access.bindings ?? [];
|
|
71806
72941
|
for (let i = 0; i < bindings.length; i++) {
|
|
71807
72942
|
const binding = bindings[i];
|
|
@@ -71813,18 +72948,73 @@ function semanticValidate(yaml) {
|
|
|
71813
72948
|
});
|
|
71814
72949
|
}
|
|
71815
72950
|
}
|
|
71816
|
-
if (binding.
|
|
72951
|
+
if (binding.project_id !== void 0 && binding.project_id.trim() === "") {
|
|
72952
|
+
errors.push({
|
|
72953
|
+
path: `access.bindings[${i}].project_id`,
|
|
72954
|
+
message: "project_id cannot be empty when provided"
|
|
72955
|
+
});
|
|
72956
|
+
}
|
|
72957
|
+
if (binding.subject.type === "group" && !groupSlugs.has(binding.subject.id)) {
|
|
72958
|
+
errors.push({
|
|
72959
|
+
path: `access.bindings[${i}].subject.id`,
|
|
72960
|
+
message: `Group '${binding.subject.id}' is not defined in access.groups`
|
|
72961
|
+
});
|
|
72962
|
+
}
|
|
72963
|
+
const normalizedScope = normalizeBindingScope(binding.scope);
|
|
72964
|
+
const requiredScopeTypes = /* @__PURE__ */ new Set();
|
|
72965
|
+
let needsWritableOrgFsScope = false;
|
|
72966
|
+
let needsWritableOrgDocsScope = false;
|
|
72967
|
+
for (const roleName of binding.roles) {
|
|
72968
|
+
const role = roles[roleName];
|
|
72969
|
+
if (!role) continue;
|
|
72970
|
+
for (const permission of role.permissions) {
|
|
72971
|
+
const scopeType = permissionScopeType(permission);
|
|
72972
|
+
if (scopeType) {
|
|
72973
|
+
requiredScopeTypes.add(scopeType);
|
|
72974
|
+
}
|
|
72975
|
+
if (permission === "orgfs:write" || permission === "orgfs:admin") {
|
|
72976
|
+
needsWritableOrgFsScope = true;
|
|
72977
|
+
}
|
|
72978
|
+
if (permission === "orgdocs:write" || permission === "orgdocs:admin") {
|
|
72979
|
+
needsWritableOrgDocsScope = true;
|
|
72980
|
+
}
|
|
72981
|
+
}
|
|
72982
|
+
}
|
|
72983
|
+
if (requiredScopeTypes.has("orgfs") && !hasPrefixScope(normalizedScope, "orgfs", needsWritableOrgFsScope)) {
|
|
72984
|
+
errors.push({
|
|
72985
|
+
path: `access.bindings[${i}].scope`,
|
|
72986
|
+
message: needsWritableOrgFsScope ? "Binding includes orgfs write/admin permissions but scope.orgfs.allow_prefixes is missing" : "Binding includes orgfs permissions but scope.orgfs prefixes are missing"
|
|
72987
|
+
});
|
|
72988
|
+
}
|
|
72989
|
+
if (requiredScopeTypes.has("orgdocs") && !hasPrefixScope(normalizedScope, "orgdocs", needsWritableOrgDocsScope)) {
|
|
71817
72990
|
errors.push({
|
|
71818
|
-
path: `access.bindings[${i}]`,
|
|
71819
|
-
message: "
|
|
72991
|
+
path: `access.bindings[${i}].scope`,
|
|
72992
|
+
message: needsWritableOrgDocsScope ? "Binding includes orgdocs write/admin permissions but scope.orgdocs.allow_prefixes is missing" : "Binding includes orgdocs permissions but scope.orgdocs prefixes are missing"
|
|
71820
72993
|
});
|
|
71821
72994
|
}
|
|
71822
|
-
if (
|
|
72995
|
+
if (requiredScopeTypes.has("envdb") && !hasEnvDbScope(normalizedScope)) {
|
|
71823
72996
|
errors.push({
|
|
71824
|
-
path: `access.bindings[${i}]`,
|
|
71825
|
-
message: "
|
|
72997
|
+
path: `access.bindings[${i}].scope`,
|
|
72998
|
+
message: "Binding includes envdb permissions but scope.envdb.schemas/tables is missing"
|
|
71826
72999
|
});
|
|
71827
73000
|
}
|
|
73001
|
+
for (const roleName of binding.roles) {
|
|
73002
|
+
const dedupeKey = bindingMatchKey(
|
|
73003
|
+
roleName,
|
|
73004
|
+
binding.subject.type,
|
|
73005
|
+
binding.subject.id,
|
|
73006
|
+
binding.project_id ?? null,
|
|
73007
|
+
normalizedScope
|
|
73008
|
+
);
|
|
73009
|
+
if (seenBindingKeys.has(dedupeKey)) {
|
|
73010
|
+
errors.push({
|
|
73011
|
+
path: `access.bindings[${i}]`,
|
|
73012
|
+
message: `Duplicate binding tuple for role '${roleName}' and subject '${binding.subject.type}:${binding.subject.id}'`
|
|
73013
|
+
});
|
|
73014
|
+
} else {
|
|
73015
|
+
seenBindingKeys.add(dedupeKey);
|
|
73016
|
+
}
|
|
73017
|
+
}
|
|
71828
73018
|
}
|
|
71829
73019
|
return errors;
|
|
71830
73020
|
}
|
|
@@ -71837,7 +73027,9 @@ async function handleValidate(flags, json) {
|
|
|
71837
73027
|
valid: errors.length === 0,
|
|
71838
73028
|
file: filePath,
|
|
71839
73029
|
errors,
|
|
73030
|
+
groups: Object.keys(yaml.access.groups ?? {}).length,
|
|
71840
73031
|
roles: Object.keys(yaml.access.roles ?? {}).length,
|
|
73032
|
+
members: Object.values(yaml.access.groups ?? {}).reduce((acc, group) => acc + (group.members?.length ?? 0), 0),
|
|
71841
73033
|
bindings: (yaml.access.bindings ?? []).length
|
|
71842
73034
|
}, json);
|
|
71843
73035
|
return;
|
|
@@ -71850,28 +73042,124 @@ async function handleValidate(flags, json) {
|
|
|
71850
73042
|
}
|
|
71851
73043
|
process.exit(1);
|
|
71852
73044
|
}
|
|
73045
|
+
const groupCount = Object.keys(yaml.access.groups ?? {}).length;
|
|
71853
73046
|
const roleCount = Object.keys(yaml.access.roles ?? {}).length;
|
|
73047
|
+
const memberCount = Object.values(yaml.access.groups ?? {}).reduce((acc, group) => acc + (group.members?.length ?? 0), 0);
|
|
71854
73048
|
const bindingCount = (yaml.access.bindings ?? []).length;
|
|
71855
|
-
console.log(`Valid (${roleCount} roles, ${bindingCount} bindings)`);
|
|
73049
|
+
console.log(`Valid (${groupCount} groups, ${memberCount} members, ${roleCount} roles, ${bindingCount} bindings)`);
|
|
71856
73050
|
}
|
|
71857
|
-
function
|
|
71858
|
-
|
|
71859
|
-
|
|
73051
|
+
function resolveGroupPrincipalId(groupRef, groupsById, groupsBySlug) {
|
|
73052
|
+
if (groupsById.has(groupRef)) {
|
|
73053
|
+
return groupRef;
|
|
73054
|
+
}
|
|
73055
|
+
return groupsBySlug.get(groupRef)?.id ?? null;
|
|
71860
73056
|
}
|
|
71861
73057
|
async function computePlan(yaml, orgId, context2) {
|
|
71862
|
-
const [apiRolesResponse, apiBindingsResponse] = await Promise.all([
|
|
73058
|
+
const [apiGroupsResponse, apiRolesResponse, apiBindingsResponse] = await Promise.all([
|
|
73059
|
+
requestJson(context2, `/orgs/${orgId}/access/groups`),
|
|
71863
73060
|
requestJson(context2, `/orgs/${orgId}/access/roles`),
|
|
71864
73061
|
requestJson(context2, `/orgs/${orgId}/access/bindings`)
|
|
71865
73062
|
]);
|
|
73063
|
+
const apiGroups = unwrapListResponse(apiGroupsResponse);
|
|
71866
73064
|
const apiRoles = unwrapListResponse(apiRolesResponse);
|
|
71867
73065
|
const apiBindings = unwrapListResponse(apiBindingsResponse);
|
|
73066
|
+
const groupMemberEntries = await Promise.all(
|
|
73067
|
+
apiGroups.map(async (group) => {
|
|
73068
|
+
const membersResponse = await requestJson(
|
|
73069
|
+
context2,
|
|
73070
|
+
`/orgs/${orgId}/access/groups/${encodeURIComponent(group.id)}/members`
|
|
73071
|
+
);
|
|
73072
|
+
return [group.id, unwrapListResponse(membersResponse)];
|
|
73073
|
+
})
|
|
73074
|
+
);
|
|
73075
|
+
const membersByGroupId = new Map(
|
|
73076
|
+
groupMemberEntries.map(([groupId, members]) => [groupId, members])
|
|
73077
|
+
);
|
|
73078
|
+
const groupsBySlug = new Map(apiGroups.map((group) => [group.slug, group]));
|
|
73079
|
+
const groupsById = new Map(apiGroups.map((group) => [group.id, group]));
|
|
71868
73080
|
const rolesByName = new Map(apiRoles.map((r) => [r.name, r]));
|
|
71869
73081
|
const plan = {
|
|
73082
|
+
groups: [],
|
|
73083
|
+
group_members: [],
|
|
71870
73084
|
roles: [],
|
|
71871
73085
|
bindings: [],
|
|
73086
|
+
unchanged_groups: 0,
|
|
73087
|
+
unchanged_group_members: 0,
|
|
71872
73088
|
unchanged_roles: 0,
|
|
71873
73089
|
unchanged_bindings: 0
|
|
71874
73090
|
};
|
|
73091
|
+
const declaredGroupSlugs = /* @__PURE__ */ new Set();
|
|
73092
|
+
for (const [slug, declaredGroup] of Object.entries(yaml.access.groups ?? {})) {
|
|
73093
|
+
declaredGroupSlugs.add(slug);
|
|
73094
|
+
const existing = groupsBySlug.get(slug);
|
|
73095
|
+
const desiredMembers = declaredGroup.members ?? [];
|
|
73096
|
+
if (!existing) {
|
|
73097
|
+
plan.groups.push({ action: "create", slug, group: declaredGroup });
|
|
73098
|
+
for (const member of desiredMembers) {
|
|
73099
|
+
plan.group_members.push({
|
|
73100
|
+
action: "add",
|
|
73101
|
+
groupSlug: slug,
|
|
73102
|
+
member
|
|
73103
|
+
});
|
|
73104
|
+
}
|
|
73105
|
+
continue;
|
|
73106
|
+
}
|
|
73107
|
+
const changes = [];
|
|
73108
|
+
const desiredName = declaredGroup.name ?? groupDefaultName(slug);
|
|
73109
|
+
const desiredDescription = declaredGroup.description ?? null;
|
|
73110
|
+
if (desiredName !== existing.name) {
|
|
73111
|
+
changes.push(`name: "${existing.name}" -> "${desiredName}"`);
|
|
73112
|
+
}
|
|
73113
|
+
if (desiredDescription !== (existing.description ?? null)) {
|
|
73114
|
+
changes.push(`description: "${existing.description ?? "(none)"}" -> "${desiredDescription ?? "(none)"}"`);
|
|
73115
|
+
}
|
|
73116
|
+
if (changes.length > 0) {
|
|
73117
|
+
plan.groups.push({
|
|
73118
|
+
action: "update",
|
|
73119
|
+
slug,
|
|
73120
|
+
id: existing.id,
|
|
73121
|
+
group: declaredGroup,
|
|
73122
|
+
changes
|
|
73123
|
+
});
|
|
73124
|
+
} else {
|
|
73125
|
+
plan.unchanged_groups++;
|
|
73126
|
+
}
|
|
73127
|
+
const existingMembers = membersByGroupId.get(existing.id) ?? [];
|
|
73128
|
+
const existingMemberKeys = new Set(existingMembers.map(apiMemberKey));
|
|
73129
|
+
const desiredMemberKeys = new Set(desiredMembers.map(declaredMemberKey));
|
|
73130
|
+
for (const member of desiredMembers) {
|
|
73131
|
+
const key = declaredMemberKey(member);
|
|
73132
|
+
if (existingMemberKeys.has(key)) {
|
|
73133
|
+
plan.unchanged_group_members++;
|
|
73134
|
+
} else {
|
|
73135
|
+
plan.group_members.push({
|
|
73136
|
+
action: "add",
|
|
73137
|
+
groupSlug: slug,
|
|
73138
|
+
member
|
|
73139
|
+
});
|
|
73140
|
+
}
|
|
73141
|
+
}
|
|
73142
|
+
for (const member of existingMembers) {
|
|
73143
|
+
const key = apiMemberKey(member);
|
|
73144
|
+
if (!desiredMemberKeys.has(key)) {
|
|
73145
|
+
plan.group_members.push({
|
|
73146
|
+
action: "remove",
|
|
73147
|
+
groupSlug: slug,
|
|
73148
|
+
groupId: existing.id,
|
|
73149
|
+
member
|
|
73150
|
+
});
|
|
73151
|
+
}
|
|
73152
|
+
}
|
|
73153
|
+
}
|
|
73154
|
+
for (const apiGroup of apiGroups) {
|
|
73155
|
+
if (!declaredGroupSlugs.has(apiGroup.slug)) {
|
|
73156
|
+
plan.groups.push({
|
|
73157
|
+
action: "prune",
|
|
73158
|
+
slug: apiGroup.slug,
|
|
73159
|
+
id: apiGroup.id
|
|
73160
|
+
});
|
|
73161
|
+
}
|
|
73162
|
+
}
|
|
71875
73163
|
const declaredRoleNames = /* @__PURE__ */ new Set();
|
|
71876
73164
|
for (const [name, declaredRole] of Object.entries(yaml.access.roles ?? {})) {
|
|
71877
73165
|
declaredRoleNames.add(name);
|
|
@@ -71908,49 +73196,146 @@ async function computePlan(yaml, orgId, context2) {
|
|
|
71908
73196
|
plan.roles.push({ action: "prune", name: apiRole.name, id: apiRole.id });
|
|
71909
73197
|
}
|
|
71910
73198
|
}
|
|
71911
|
-
const
|
|
73199
|
+
const existingBindingByMatchKey = /* @__PURE__ */ new Map();
|
|
73200
|
+
const existingBindingByIdentityKey = /* @__PURE__ */ new Map();
|
|
71912
73201
|
for (const ab of apiBindings) {
|
|
71913
|
-
const
|
|
71914
|
-
|
|
73202
|
+
const identityKey = bindingIdentityKey(ab.role_name, ab.principal_type, ab.principal_id, ab.project_id);
|
|
73203
|
+
existingBindingByIdentityKey.set(identityKey, ab);
|
|
73204
|
+
const matchKey = bindingMatchKey(
|
|
73205
|
+
ab.role_name,
|
|
73206
|
+
ab.principal_type,
|
|
73207
|
+
ab.principal_id,
|
|
73208
|
+
ab.project_id,
|
|
73209
|
+
ab.scope_json
|
|
73210
|
+
);
|
|
73211
|
+
existingBindingByMatchKey.set(matchKey, ab);
|
|
71915
73212
|
}
|
|
71916
|
-
const
|
|
73213
|
+
const matchedApiBindingIds = /* @__PURE__ */ new Set();
|
|
73214
|
+
const groupsMarkedForPrune = new Set(
|
|
73215
|
+
plan.groups.filter((group) => group.action === "prune").map((group) => group.id)
|
|
73216
|
+
);
|
|
71917
73217
|
for (const declaredBinding of yaml.access.bindings ?? []) {
|
|
71918
|
-
|
|
71919
|
-
|
|
71920
|
-
|
|
73218
|
+
const normalizedDeclaredScope = normalizeBindingScope(declaredBinding.scope);
|
|
73219
|
+
const principalType = declaredBinding.subject.type;
|
|
73220
|
+
let principalIdHint = declaredBinding.subject.id;
|
|
73221
|
+
if (principalType === "group") {
|
|
73222
|
+
const resolvedGroupId = resolveGroupPrincipalId(declaredBinding.subject.id, groupsById, groupsBySlug);
|
|
73223
|
+
if (resolvedGroupId) {
|
|
73224
|
+
principalIdHint = resolvedGroupId;
|
|
73225
|
+
}
|
|
73226
|
+
}
|
|
73227
|
+
for (const roleName of [...new Set(declaredBinding.roles)]) {
|
|
73228
|
+
const identityKey = bindingIdentityKey(
|
|
71921
73229
|
roleName,
|
|
71922
|
-
|
|
71923
|
-
|
|
71924
|
-
|
|
73230
|
+
principalType,
|
|
73231
|
+
principalIdHint,
|
|
73232
|
+
declaredBinding.project_id ?? null
|
|
71925
73233
|
);
|
|
71926
|
-
|
|
71927
|
-
|
|
73234
|
+
const matchKey = bindingMatchKey(
|
|
73235
|
+
roleName,
|
|
73236
|
+
principalType,
|
|
73237
|
+
principalIdHint,
|
|
73238
|
+
declaredBinding.project_id ?? null,
|
|
73239
|
+
normalizedDeclaredScope
|
|
73240
|
+
);
|
|
73241
|
+
const exactMatch = existingBindingByMatchKey.get(matchKey);
|
|
73242
|
+
if (exactMatch) {
|
|
73243
|
+
matchedApiBindingIds.add(exactMatch.id);
|
|
71928
73244
|
plan.unchanged_bindings++;
|
|
71929
|
-
|
|
71930
|
-
plan.bindings.push({ action: "create", binding: declaredBinding, roleName });
|
|
73245
|
+
continue;
|
|
71931
73246
|
}
|
|
73247
|
+
const identityMatch = existingBindingByIdentityKey.get(identityKey);
|
|
73248
|
+
if (identityMatch) {
|
|
73249
|
+
matchedApiBindingIds.add(identityMatch.id);
|
|
73250
|
+
const changes = [];
|
|
73251
|
+
if (!scopeEquals(identityMatch.scope_json, normalizedDeclaredScope)) {
|
|
73252
|
+
changes.push(
|
|
73253
|
+
`scope: ${formatBindingScope(identityMatch.scope_json)} -> ${formatBindingScope(normalizedDeclaredScope)}`
|
|
73254
|
+
);
|
|
73255
|
+
}
|
|
73256
|
+
plan.bindings.push({
|
|
73257
|
+
action: "replace",
|
|
73258
|
+
existing: identityMatch,
|
|
73259
|
+
binding: declaredBinding,
|
|
73260
|
+
roleName,
|
|
73261
|
+
principalIdHint,
|
|
73262
|
+
changes
|
|
73263
|
+
});
|
|
73264
|
+
continue;
|
|
73265
|
+
}
|
|
73266
|
+
plan.bindings.push({
|
|
73267
|
+
action: "create",
|
|
73268
|
+
binding: declaredBinding,
|
|
73269
|
+
roleName,
|
|
73270
|
+
principalIdHint
|
|
73271
|
+
});
|
|
71932
73272
|
}
|
|
71933
73273
|
}
|
|
71934
|
-
for (const
|
|
71935
|
-
if (
|
|
71936
|
-
|
|
73274
|
+
for (const ab of apiBindings) {
|
|
73275
|
+
if (matchedApiBindingIds.has(ab.id)) {
|
|
73276
|
+
continue;
|
|
71937
73277
|
}
|
|
73278
|
+
if (ab.principal_type === "group" && groupsMarkedForPrune.has(ab.principal_id)) {
|
|
73279
|
+
continue;
|
|
73280
|
+
}
|
|
73281
|
+
plan.bindings.push({ action: "prune", binding: ab });
|
|
71938
73282
|
}
|
|
71939
73283
|
return plan;
|
|
71940
73284
|
}
|
|
71941
73285
|
function hasChanges(plan, prune) {
|
|
73286
|
+
const groupChanges = plan.groups.filter(
|
|
73287
|
+
(g) => g.action === "create" || g.action === "update" || g.action === "prune" && prune
|
|
73288
|
+
);
|
|
73289
|
+
const memberChanges = plan.group_members.filter(
|
|
73290
|
+
(m) => m.action === "add" || m.action === "remove"
|
|
73291
|
+
);
|
|
71942
73292
|
const roleChanges = plan.roles.filter(
|
|
71943
73293
|
(r) => r.action === "create" || r.action === "update" || r.action === "prune" && prune
|
|
71944
73294
|
);
|
|
71945
73295
|
const bindingChanges = plan.bindings.filter(
|
|
71946
|
-
(b) => b.action === "create" || b.action === "prune" && prune
|
|
73296
|
+
(b) => b.action === "create" || b.action === "replace" || b.action === "prune" && prune
|
|
71947
73297
|
);
|
|
71948
|
-
return roleChanges.length > 0 || bindingChanges.length > 0;
|
|
73298
|
+
return groupChanges.length > 0 || memberChanges.length > 0 || roleChanges.length > 0 || bindingChanges.length > 0;
|
|
71949
73299
|
}
|
|
71950
73300
|
function printPlan(plan, orgId, prune) {
|
|
71951
73301
|
console.log(`
|
|
71952
73302
|
Access Plan for ${orgId}:
|
|
71953
73303
|
`);
|
|
73304
|
+
const groupCreates = plan.groups.filter((g) => g.action === "create");
|
|
73305
|
+
const groupUpdates = plan.groups.filter((g) => g.action === "update");
|
|
73306
|
+
const groupPrunes = plan.groups.filter((g) => g.action === "prune");
|
|
73307
|
+
if (groupCreates.length > 0 || groupUpdates.length > 0 || groupPrunes.length > 0) {
|
|
73308
|
+
console.log("Groups:");
|
|
73309
|
+
for (const group of groupCreates) {
|
|
73310
|
+
const name = group.group.name ?? groupDefaultName(group.slug);
|
|
73311
|
+
console.log(` + CREATE ${group.slug} (${name})`);
|
|
73312
|
+
}
|
|
73313
|
+
for (const group of groupUpdates) {
|
|
73314
|
+
console.log(` ~ UPDATE ${group.slug}: ${group.changes.join("; ")}`);
|
|
73315
|
+
}
|
|
73316
|
+
if (groupPrunes.length > 0) {
|
|
73317
|
+
if (prune) {
|
|
73318
|
+
for (const group of groupPrunes) {
|
|
73319
|
+
console.log(` - DELETE ${group.slug} (${group.id})`);
|
|
73320
|
+
}
|
|
73321
|
+
} else {
|
|
73322
|
+
console.log(` ? Undeclared (not pruned): ${groupPrunes.map((group) => group.slug).join(", ")}`);
|
|
73323
|
+
}
|
|
73324
|
+
}
|
|
73325
|
+
console.log("");
|
|
73326
|
+
}
|
|
73327
|
+
const memberAdds = plan.group_members.filter((m) => m.action === "add");
|
|
73328
|
+
const memberRemoves = plan.group_members.filter((m) => m.action === "remove");
|
|
73329
|
+
if (memberAdds.length > 0 || memberRemoves.length > 0) {
|
|
73330
|
+
console.log("Group Memberships:");
|
|
73331
|
+
for (const member of memberAdds) {
|
|
73332
|
+
console.log(` + ADD ${member.member.type}:${member.member.id} -> ${member.groupSlug}`);
|
|
73333
|
+
}
|
|
73334
|
+
for (const member of memberRemoves) {
|
|
73335
|
+
console.log(` - REMOVE ${member.member.principal_type}:${member.member.principal_id} -> ${member.groupSlug}`);
|
|
73336
|
+
}
|
|
73337
|
+
console.log("");
|
|
73338
|
+
}
|
|
71954
73339
|
const roleCreates = plan.roles.filter((r) => r.action === "create");
|
|
71955
73340
|
const roleUpdates = plan.roles.filter((r) => r.action === "update");
|
|
71956
73341
|
const rolePrunes = plan.roles.filter((r) => r.action === "prune");
|
|
@@ -71974,12 +73359,21 @@ Access Plan for ${orgId}:
|
|
|
71974
73359
|
console.log("");
|
|
71975
73360
|
}
|
|
71976
73361
|
const bindingCreates = plan.bindings.filter((b) => b.action === "create");
|
|
73362
|
+
const bindingReplaces = plan.bindings.filter((b) => b.action === "replace");
|
|
71977
73363
|
const bindingPrunes = plan.bindings.filter((b) => b.action === "prune");
|
|
71978
|
-
if (bindingCreates.length > 0 || bindingPrunes.length > 0) {
|
|
73364
|
+
if (bindingCreates.length > 0 || bindingReplaces.length > 0 || bindingPrunes.length > 0) {
|
|
71979
73365
|
console.log("Bindings:");
|
|
71980
73366
|
for (const b of bindingCreates) {
|
|
71981
|
-
const scopeLabel = b.binding.
|
|
71982
|
-
console.log(
|
|
73367
|
+
const scopeLabel = b.binding.project_id ? `project: ${b.binding.project_id}` : "org-wide";
|
|
73368
|
+
console.log(
|
|
73369
|
+
` + BIND ${b.roleName} -> ${b.binding.subject.type}:${b.binding.subject.id} (${scopeLabel}, scope=${formatBindingScope(b.binding.scope)})`
|
|
73370
|
+
);
|
|
73371
|
+
}
|
|
73372
|
+
for (const b of bindingReplaces) {
|
|
73373
|
+
const scopeLabel = b.binding.project_id ? `project: ${b.binding.project_id}` : "org-wide";
|
|
73374
|
+
console.log(
|
|
73375
|
+
` ~ REPLACE ${b.roleName} -> ${b.binding.subject.type}:${b.binding.subject.id} (${scopeLabel}): ${b.changes.join("; ")}`
|
|
73376
|
+
);
|
|
71983
73377
|
}
|
|
71984
73378
|
if (bindingPrunes.length > 0) {
|
|
71985
73379
|
if (prune) {
|
|
@@ -71993,9 +73387,11 @@ Access Plan for ${orgId}:
|
|
|
71993
73387
|
}
|
|
71994
73388
|
console.log("");
|
|
71995
73389
|
}
|
|
71996
|
-
const totalUnchanged = plan.unchanged_roles + plan.unchanged_bindings;
|
|
73390
|
+
const totalUnchanged = plan.unchanged_groups + plan.unchanged_group_members + plan.unchanged_roles + plan.unchanged_bindings;
|
|
71997
73391
|
if (totalUnchanged > 0) {
|
|
71998
|
-
console.log(
|
|
73392
|
+
console.log(
|
|
73393
|
+
`Unchanged: ${plan.unchanged_groups} group(s), ${plan.unchanged_group_members} member(s), ${plan.unchanged_roles} role(s), ${plan.unchanged_bindings} binding(s)`
|
|
73394
|
+
);
|
|
71999
73395
|
}
|
|
72000
73396
|
if (!hasChanges(plan, prune)) {
|
|
72001
73397
|
console.log("No changes needed.");
|
|
@@ -72004,6 +73400,52 @@ Access Plan for ${orgId}:
|
|
|
72004
73400
|
function planToJson(plan, orgId) {
|
|
72005
73401
|
return {
|
|
72006
73402
|
org_id: orgId,
|
|
73403
|
+
groups: {
|
|
73404
|
+
create: plan.groups.filter((group) => group.action === "create").map((group) => {
|
|
73405
|
+
const create = group;
|
|
73406
|
+
return {
|
|
73407
|
+
slug: create.slug,
|
|
73408
|
+
name: create.group.name ?? groupDefaultName(create.slug),
|
|
73409
|
+
description: create.group.description ?? null
|
|
73410
|
+
};
|
|
73411
|
+
}),
|
|
73412
|
+
update: plan.groups.filter((group) => group.action === "update").map((group) => {
|
|
73413
|
+
const update = group;
|
|
73414
|
+
return {
|
|
73415
|
+
slug: update.slug,
|
|
73416
|
+
id: update.id,
|
|
73417
|
+
changes: update.changes
|
|
73418
|
+
};
|
|
73419
|
+
}),
|
|
73420
|
+
prune: plan.groups.filter((group) => group.action === "prune").map((group) => {
|
|
73421
|
+
const prune = group;
|
|
73422
|
+
return {
|
|
73423
|
+
slug: prune.slug,
|
|
73424
|
+
id: prune.id
|
|
73425
|
+
};
|
|
73426
|
+
}),
|
|
73427
|
+
unchanged: plan.unchanged_groups
|
|
73428
|
+
},
|
|
73429
|
+
group_members: {
|
|
73430
|
+
add: plan.group_members.filter((member) => member.action === "add").map((member) => {
|
|
73431
|
+
const add = member;
|
|
73432
|
+
return {
|
|
73433
|
+
group_slug: add.groupSlug,
|
|
73434
|
+
principal_type: add.member.type,
|
|
73435
|
+
principal_id: add.member.id
|
|
73436
|
+
};
|
|
73437
|
+
}),
|
|
73438
|
+
remove: plan.group_members.filter((member) => member.action === "remove").map((member) => {
|
|
73439
|
+
const remove = member;
|
|
73440
|
+
return {
|
|
73441
|
+
group_slug: remove.groupSlug,
|
|
73442
|
+
group_id: remove.groupId,
|
|
73443
|
+
principal_type: remove.member.principal_type,
|
|
73444
|
+
principal_id: remove.member.principal_id
|
|
73445
|
+
};
|
|
73446
|
+
}),
|
|
73447
|
+
unchanged: plan.unchanged_group_members
|
|
73448
|
+
},
|
|
72007
73449
|
roles: {
|
|
72008
73450
|
create: plan.roles.filter((r) => r.action === "create").map((r) => {
|
|
72009
73451
|
const cr = r;
|
|
@@ -72026,10 +73468,22 @@ function planToJson(plan, orgId) {
|
|
|
72026
73468
|
role: cb.roleName,
|
|
72027
73469
|
subject_type: cb.binding.subject.type,
|
|
72028
73470
|
subject_id: cb.binding.subject.id,
|
|
72029
|
-
scope: cb.binding.scope,
|
|
73471
|
+
scope: normalizeBindingScope(cb.binding.scope) ?? null,
|
|
72030
73472
|
project_id: cb.binding.project_id
|
|
72031
73473
|
};
|
|
72032
73474
|
}),
|
|
73475
|
+
replace: plan.bindings.filter((b) => b.action === "replace").map((b) => {
|
|
73476
|
+
const rb = b;
|
|
73477
|
+
return {
|
|
73478
|
+
id: rb.existing.id,
|
|
73479
|
+
role: rb.roleName,
|
|
73480
|
+
subject_type: rb.binding.subject.type,
|
|
73481
|
+
subject_id: rb.binding.subject.id,
|
|
73482
|
+
scope: normalizeBindingScope(rb.binding.scope) ?? null,
|
|
73483
|
+
project_id: rb.binding.project_id,
|
|
73484
|
+
changes: rb.changes
|
|
73485
|
+
};
|
|
73486
|
+
}),
|
|
72033
73487
|
prune: plan.bindings.filter((b) => b.action === "prune").map((b) => {
|
|
72034
73488
|
const pb = b;
|
|
72035
73489
|
return {
|
|
@@ -72064,6 +73518,26 @@ ${lines.join("\n")}`);
|
|
|
72064
73518
|
}
|
|
72065
73519
|
printPlan(plan, orgId, false);
|
|
72066
73520
|
}
|
|
73521
|
+
async function fetchOrgGroups(orgId, context2) {
|
|
73522
|
+
const groupsResponse = await requestJson(
|
|
73523
|
+
context2,
|
|
73524
|
+
`/orgs/${orgId}/access/groups`
|
|
73525
|
+
);
|
|
73526
|
+
return unwrapListResponse(groupsResponse);
|
|
73527
|
+
}
|
|
73528
|
+
function resolveBindingPrincipalIdForApply(principalType, principalIdHint, groupIdsBySlug, groupIds) {
|
|
73529
|
+
if (principalType !== "group") {
|
|
73530
|
+
return principalIdHint;
|
|
73531
|
+
}
|
|
73532
|
+
if (groupIds.has(principalIdHint)) {
|
|
73533
|
+
return principalIdHint;
|
|
73534
|
+
}
|
|
73535
|
+
const fromSlug = groupIdsBySlug.get(principalIdHint);
|
|
73536
|
+
if (fromSlug) {
|
|
73537
|
+
return fromSlug;
|
|
73538
|
+
}
|
|
73539
|
+
throw new Error(`Group '${principalIdHint}' does not exist in the target org`);
|
|
73540
|
+
}
|
|
72067
73541
|
async function handleSync(flags, context2, json) {
|
|
72068
73542
|
const orgId = getStringFlag(flags, ["org"]) ?? context2.orgId;
|
|
72069
73543
|
if (!orgId) {
|
|
@@ -72103,10 +73577,16 @@ ${lines.join("\n")}`);
|
|
|
72103
73577
|
}
|
|
72104
73578
|
}
|
|
72105
73579
|
const applied = {
|
|
73580
|
+
groups_created: 0,
|
|
73581
|
+
groups_updated: 0,
|
|
73582
|
+
groups_deleted: 0,
|
|
73583
|
+
group_members_added: 0,
|
|
73584
|
+
group_members_removed: 0,
|
|
72106
73585
|
roles_created: 0,
|
|
72107
73586
|
roles_updated: 0,
|
|
72108
73587
|
roles_deleted: 0,
|
|
72109
73588
|
bindings_created: 0,
|
|
73589
|
+
bindings_replaced: 0,
|
|
72110
73590
|
bindings_deleted: 0
|
|
72111
73591
|
};
|
|
72112
73592
|
for (const ra of plan.roles) {
|
|
@@ -72146,25 +73626,123 @@ ${lines.join("\n")}`);
|
|
|
72146
73626
|
if (!json) console.log(` ~ Updated role '${ra.name}'`);
|
|
72147
73627
|
}
|
|
72148
73628
|
}
|
|
72149
|
-
for (const
|
|
72150
|
-
if (
|
|
72151
|
-
const
|
|
72152
|
-
|
|
72153
|
-
|
|
72154
|
-
|
|
73629
|
+
for (const ga of plan.groups) {
|
|
73630
|
+
if (ga.action === "create") {
|
|
73631
|
+
const payload = {
|
|
73632
|
+
name: ga.group.name ?? groupDefaultName(ga.slug),
|
|
73633
|
+
slug: ga.slug,
|
|
73634
|
+
description: ga.group.description
|
|
73635
|
+
};
|
|
73636
|
+
await requestJson(
|
|
73637
|
+
context2,
|
|
73638
|
+
`/orgs/${orgId}/access/groups`,
|
|
73639
|
+
{
|
|
73640
|
+
method: "POST",
|
|
73641
|
+
body: payload
|
|
73642
|
+
}
|
|
73643
|
+
);
|
|
73644
|
+
applied.groups_created++;
|
|
73645
|
+
if (!json) console.log(` + Created group '${ga.slug}'`);
|
|
73646
|
+
continue;
|
|
73647
|
+
}
|
|
73648
|
+
if (ga.action === "update") {
|
|
73649
|
+
const payload = {
|
|
73650
|
+
name: ga.group.name ?? groupDefaultName(ga.slug),
|
|
73651
|
+
description: ga.group.description ?? null
|
|
72155
73652
|
};
|
|
72156
|
-
|
|
72157
|
-
|
|
73653
|
+
await requestJson(
|
|
73654
|
+
context2,
|
|
73655
|
+
`/orgs/${orgId}/access/groups/${encodeURIComponent(ga.id)}`,
|
|
73656
|
+
{
|
|
73657
|
+
method: "PATCH",
|
|
73658
|
+
body: payload
|
|
73659
|
+
}
|
|
73660
|
+
);
|
|
73661
|
+
applied.groups_updated++;
|
|
73662
|
+
if (!json) console.log(` ~ Updated group '${ga.slug}'`);
|
|
73663
|
+
}
|
|
73664
|
+
}
|
|
73665
|
+
const groupsAfterCreateUpdate = await fetchOrgGroups(orgId, context2);
|
|
73666
|
+
const groupIdsBySlug = new Map(groupsAfterCreateUpdate.map((group) => [group.slug, group.id]));
|
|
73667
|
+
const groupIds = new Set(groupsAfterCreateUpdate.map((group) => group.id));
|
|
73668
|
+
for (const ma of plan.group_members) {
|
|
73669
|
+
if (ma.action === "add") {
|
|
73670
|
+
const groupId2 = groupIdsBySlug.get(ma.groupSlug);
|
|
73671
|
+
if (!groupId2) {
|
|
73672
|
+
throw new Error(`Cannot add member: group '${ma.groupSlug}' does not exist in org '${orgId}'`);
|
|
72158
73673
|
}
|
|
72159
73674
|
await requestJson(
|
|
72160
73675
|
context2,
|
|
72161
|
-
`/orgs/${orgId}/access/
|
|
72162
|
-
{
|
|
73676
|
+
`/orgs/${orgId}/access/groups/${encodeURIComponent(groupId2)}/members`,
|
|
73677
|
+
{
|
|
73678
|
+
method: "POST",
|
|
73679
|
+
body: {
|
|
73680
|
+
principal_type: ma.member.type,
|
|
73681
|
+
principal_id: ma.member.id
|
|
73682
|
+
}
|
|
73683
|
+
}
|
|
73684
|
+
);
|
|
73685
|
+
applied.group_members_added++;
|
|
73686
|
+
if (!json) console.log(` + Added ${ma.member.type}:${ma.member.id} to group '${ma.groupSlug}'`);
|
|
73687
|
+
continue;
|
|
73688
|
+
}
|
|
73689
|
+
const groupId = groupIdsBySlug.get(ma.groupSlug) ?? ma.groupId;
|
|
73690
|
+
await requestJson(
|
|
73691
|
+
context2,
|
|
73692
|
+
`/orgs/${orgId}/access/groups/${encodeURIComponent(groupId)}/members/${ma.member.principal_type}/${encodeURIComponent(ma.member.principal_id)}`,
|
|
73693
|
+
{ method: "DELETE" }
|
|
73694
|
+
);
|
|
73695
|
+
applied.group_members_removed++;
|
|
73696
|
+
if (!json) console.log(` - Removed ${ma.member.principal_type}:${ma.member.principal_id} from group '${ma.groupSlug}'`);
|
|
73697
|
+
}
|
|
73698
|
+
for (const ba of plan.bindings) {
|
|
73699
|
+
if (ba.action !== "create" && ba.action !== "replace") {
|
|
73700
|
+
continue;
|
|
73701
|
+
}
|
|
73702
|
+
const principalId = resolveBindingPrincipalIdForApply(
|
|
73703
|
+
ba.binding.subject.type,
|
|
73704
|
+
ba.principalIdHint,
|
|
73705
|
+
groupIdsBySlug,
|
|
73706
|
+
groupIds
|
|
73707
|
+
);
|
|
73708
|
+
if (ba.action === "replace") {
|
|
73709
|
+
await requestJson(
|
|
73710
|
+
context2,
|
|
73711
|
+
`/orgs/${orgId}/access/bindings/${ba.existing.id}`,
|
|
73712
|
+
{ method: "DELETE" }
|
|
72163
73713
|
);
|
|
73714
|
+
}
|
|
73715
|
+
const body = {
|
|
73716
|
+
role_name: ba.roleName,
|
|
73717
|
+
principal_type: ba.binding.subject.type,
|
|
73718
|
+
principal_id: principalId
|
|
73719
|
+
};
|
|
73720
|
+
if (ba.binding.project_id) {
|
|
73721
|
+
body.project_id = ba.binding.project_id;
|
|
73722
|
+
}
|
|
73723
|
+
const normalizedScope = normalizeBindingScope(ba.binding.scope);
|
|
73724
|
+
if (normalizedScope) {
|
|
73725
|
+
body.scope_json = normalizedScope;
|
|
73726
|
+
}
|
|
73727
|
+
await requestJson(
|
|
73728
|
+
context2,
|
|
73729
|
+
`/orgs/${orgId}/access/bindings`,
|
|
73730
|
+
{ method: "POST", body }
|
|
73731
|
+
);
|
|
73732
|
+
const scopeLabel = ba.binding.project_id ? `project: ${ba.binding.project_id}` : "org-wide";
|
|
73733
|
+
if (ba.action === "create") {
|
|
72164
73734
|
applied.bindings_created++;
|
|
72165
73735
|
if (!json) {
|
|
72166
|
-
|
|
72167
|
-
|
|
73736
|
+
console.log(
|
|
73737
|
+
` + Bound ${ba.roleName} -> ${ba.binding.subject.type}:${principalId} (${scopeLabel}, scope=${formatBindingScope(ba.binding.scope)})`
|
|
73738
|
+
);
|
|
73739
|
+
}
|
|
73740
|
+
} else {
|
|
73741
|
+
applied.bindings_replaced++;
|
|
73742
|
+
if (!json) {
|
|
73743
|
+
console.log(
|
|
73744
|
+
` ~ Rebound ${ba.roleName} -> ${ba.binding.subject.type}:${principalId} (${scopeLabel}, scope=${formatBindingScope(ba.binding.scope)})`
|
|
73745
|
+
);
|
|
72168
73746
|
}
|
|
72169
73747
|
}
|
|
72170
73748
|
}
|
|
@@ -72183,6 +73761,17 @@ ${lines.join("\n")}`);
|
|
|
72183
73761
|
}
|
|
72184
73762
|
}
|
|
72185
73763
|
}
|
|
73764
|
+
for (const ga of plan.groups) {
|
|
73765
|
+
if (ga.action === "prune") {
|
|
73766
|
+
await requestJson(
|
|
73767
|
+
context2,
|
|
73768
|
+
`/orgs/${orgId}/access/groups/${encodeURIComponent(ga.id)}`,
|
|
73769
|
+
{ method: "DELETE" }
|
|
73770
|
+
);
|
|
73771
|
+
applied.groups_deleted++;
|
|
73772
|
+
if (!json) console.log(` - Deleted group '${ga.slug}'`);
|
|
73773
|
+
}
|
|
73774
|
+
}
|
|
72186
73775
|
for (const ra of plan.roles) {
|
|
72187
73776
|
if (ra.action === "prune") {
|
|
72188
73777
|
await requestJson(
|
|
@@ -72200,18 +73789,24 @@ ${lines.join("\n")}`);
|
|
|
72200
73789
|
return;
|
|
72201
73790
|
}
|
|
72202
73791
|
const parts = [];
|
|
73792
|
+
if (applied.groups_created > 0) parts.push(`${applied.groups_created} group(s) created`);
|
|
73793
|
+
if (applied.groups_updated > 0) parts.push(`${applied.groups_updated} group(s) updated`);
|
|
73794
|
+
if (applied.groups_deleted > 0) parts.push(`${applied.groups_deleted} group(s) deleted`);
|
|
73795
|
+
if (applied.group_members_added > 0) parts.push(`${applied.group_members_added} group member(s) added`);
|
|
73796
|
+
if (applied.group_members_removed > 0) parts.push(`${applied.group_members_removed} group member(s) removed`);
|
|
72203
73797
|
if (applied.roles_created > 0) parts.push(`${applied.roles_created} role(s) created`);
|
|
72204
73798
|
if (applied.roles_updated > 0) parts.push(`${applied.roles_updated} role(s) updated`);
|
|
72205
73799
|
if (applied.roles_deleted > 0) parts.push(`${applied.roles_deleted} role(s) deleted`);
|
|
72206
73800
|
if (applied.bindings_created > 0) parts.push(`${applied.bindings_created} binding(s) created`);
|
|
73801
|
+
if (applied.bindings_replaced > 0) parts.push(`${applied.bindings_replaced} binding(s) replaced`);
|
|
72207
73802
|
if (applied.bindings_deleted > 0) parts.push(`${applied.bindings_deleted} binding(s) deleted`);
|
|
72208
73803
|
console.log(`
|
|
72209
73804
|
Sync complete: ${parts.join(", ")}`);
|
|
72210
73805
|
}
|
|
72211
73806
|
|
|
72212
73807
|
// src/commands/docs.ts
|
|
72213
|
-
var
|
|
72214
|
-
var
|
|
73808
|
+
var import_node_fs16 = require("node:fs");
|
|
73809
|
+
var import_node_path16 = require("node:path");
|
|
72215
73810
|
function encodeDocPathParam(path6) {
|
|
72216
73811
|
const trimmed = path6.startsWith("/") ? path6.slice(1) : path6;
|
|
72217
73812
|
return encodeURIComponent(trimmed);
|
|
@@ -72270,9 +73865,9 @@ async function handleDocs(subcommand, positionals, flags, context2) {
|
|
|
72270
73865
|
const filePath = getStringFlag(flags, ["file"]);
|
|
72271
73866
|
const useStdin = flags.stdin === true || flags.stdin === "true";
|
|
72272
73867
|
if (filePath) {
|
|
72273
|
-
content = (0,
|
|
73868
|
+
content = (0, import_node_fs16.readFileSync)((0, import_node_path16.resolve)(filePath), "utf-8");
|
|
72274
73869
|
} else if (useStdin) {
|
|
72275
|
-
content = (0,
|
|
73870
|
+
content = (0, import_node_fs16.readFileSync)(0, "utf-8");
|
|
72276
73871
|
} else {
|
|
72277
73872
|
throw new Error("Provide --file <path> or --stdin to supply document content");
|
|
72278
73873
|
}
|
|
@@ -72721,6 +74316,9 @@ function formatDuration2(seconds) {
|
|
|
72721
74316
|
function pad(label, value, width = 12) {
|
|
72722
74317
|
return ` ${label.padEnd(width)}${value}`;
|
|
72723
74318
|
}
|
|
74319
|
+
function parseAvgDurationSeconds(stats) {
|
|
74320
|
+
return stats.avg_duration_seconds ?? stats.avg_duration_s ?? 0;
|
|
74321
|
+
}
|
|
72724
74322
|
async function analyticsSummary(flags, context2, orgId) {
|
|
72725
74323
|
const json = Boolean(flags.json);
|
|
72726
74324
|
const window2 = getStringFlag(flags, ["window"]) ?? "7d";
|
|
@@ -72747,7 +74345,7 @@ async function analyticsSummary(flags, context2, orgId) {
|
|
|
72747
74345
|
console.log("Pipelines");
|
|
72748
74346
|
console.log(pad("Runs:", pipelines.runs));
|
|
72749
74347
|
console.log(pad("Success Rate:", `${pipelines.success_rate.toFixed(1)}%`));
|
|
72750
|
-
console.log(pad("Avg Duration:", formatDuration2(pipelines
|
|
74348
|
+
console.log(pad("Avg Duration:", formatDuration2(parseAvgDurationSeconds(pipelines))));
|
|
72751
74349
|
console.log("");
|
|
72752
74350
|
console.log("Environments");
|
|
72753
74351
|
console.log(pad("Total:", environments.total));
|
|
@@ -72766,17 +74364,15 @@ async function analyticsJobs(flags, context2, orgId) {
|
|
|
72766
74364
|
outputJson(data, true);
|
|
72767
74365
|
return;
|
|
72768
74366
|
}
|
|
72769
|
-
console.log(`Job Analytics (${
|
|
74367
|
+
console.log(`Job Analytics (${window2} window)`);
|
|
72770
74368
|
console.log("\u2550".repeat(36));
|
|
72771
|
-
|
|
72772
|
-
|
|
72773
|
-
|
|
72774
|
-
|
|
72775
|
-
|
|
72776
|
-
|
|
72777
|
-
|
|
72778
|
-
console.log(` ${job.id} ${job.phase}${duration}${desc}`);
|
|
72779
|
-
}
|
|
74369
|
+
console.log(pad("As of:", data.as_of));
|
|
74370
|
+
console.log("");
|
|
74371
|
+
console.log("Jobs");
|
|
74372
|
+
console.log(pad("Created:", data.created));
|
|
74373
|
+
console.log(pad("Completed:", data.completed));
|
|
74374
|
+
console.log(pad("Failed:", data.failed));
|
|
74375
|
+
console.log(pad("Active:", data.active));
|
|
72780
74376
|
}
|
|
72781
74377
|
async function analyticsPipelines(flags, context2, orgId) {
|
|
72782
74378
|
const json = Boolean(flags.json);
|
|
@@ -72789,22 +74385,14 @@ async function analyticsPipelines(flags, context2, orgId) {
|
|
|
72789
74385
|
outputJson(data, true);
|
|
72790
74386
|
return;
|
|
72791
74387
|
}
|
|
72792
|
-
console.log(`Pipeline Analytics (${
|
|
74388
|
+
console.log(`Pipeline Analytics (${window2} window)`);
|
|
72793
74389
|
console.log("\u2550".repeat(36));
|
|
72794
|
-
|
|
72795
|
-
|
|
72796
|
-
|
|
72797
|
-
|
|
72798
|
-
|
|
72799
|
-
|
|
72800
|
-
console.log(pad("Runs:", p.runs, 16));
|
|
72801
|
-
console.log(pad("Success Rate:", `${p.success_rate.toFixed(1)}%`, 16));
|
|
72802
|
-
console.log(pad("Avg Duration:", formatDuration2(p.avg_duration_seconds), 16));
|
|
72803
|
-
if (p.last_run_at) {
|
|
72804
|
-
console.log(pad("Last Run:", p.last_run_at, 16));
|
|
72805
|
-
}
|
|
72806
|
-
console.log("");
|
|
72807
|
-
}
|
|
74390
|
+
console.log(pad("As of:", data.as_of));
|
|
74391
|
+
console.log("");
|
|
74392
|
+
console.log("Pipelines");
|
|
74393
|
+
console.log(pad("Runs:", data.runs));
|
|
74394
|
+
console.log(pad("Success Rate:", `${data.success_rate.toFixed(1)}%`));
|
|
74395
|
+
console.log(pad("Avg Duration:", formatDuration2(parseAvgDurationSeconds(data))));
|
|
72808
74396
|
}
|
|
72809
74397
|
async function analyticsEnvHealth(flags, context2, orgId) {
|
|
72810
74398
|
const json = Boolean(flags.json);
|
|
@@ -72818,15 +74406,12 @@ async function analyticsEnvHealth(flags, context2, orgId) {
|
|
|
72818
74406
|
}
|
|
72819
74407
|
console.log("Environment Health");
|
|
72820
74408
|
console.log("\u2550".repeat(36));
|
|
72821
|
-
|
|
72822
|
-
|
|
72823
|
-
|
|
72824
|
-
|
|
72825
|
-
|
|
72826
|
-
|
|
72827
|
-
const pods = env.pod_count != null ? ` pods: ${env.pod_count}` : "";
|
|
72828
|
-
console.log(` ${env.name} [${env.status}]${pods}${lastDeploy}`);
|
|
72829
|
-
}
|
|
74409
|
+
console.log("");
|
|
74410
|
+
console.log(pad("As of:", data.as_of));
|
|
74411
|
+
console.log(pad("Total:", data.total));
|
|
74412
|
+
console.log(pad("Healthy:", data.healthy));
|
|
74413
|
+
console.log(pad("Degraded:", data.degraded));
|
|
74414
|
+
console.log(pad("Unknown:", data.unknown));
|
|
72830
74415
|
}
|
|
72831
74416
|
async function handleAnalytics(subcommand, _positionals, flags, context2) {
|
|
72832
74417
|
const orgId = getStringFlag(flags, ["org", "org-id", "org_id"]) ?? context2.orgId;
|
|
@@ -72844,7 +74429,7 @@ async function handleAnalytics(subcommand, _positionals, flags, context2) {
|
|
|
72844
74429
|
return analyticsEnvHealth(flags, context2, orgId);
|
|
72845
74430
|
default:
|
|
72846
74431
|
throw new Error(
|
|
72847
|
-
"Usage: eve analytics <summary|jobs|pipelines|env-health>\n\n summary Org-wide activity summary\n jobs Job
|
|
74432
|
+
"Usage: eve analytics <summary|jobs|pipelines|env-health>\n\n summary Org-wide activity summary\n jobs Job counters for the window\n pipelines Pipeline success rates and durations\n env-health Current environment health snapshot"
|
|
72848
74433
|
);
|
|
72849
74434
|
}
|
|
72850
74435
|
}
|
|
@@ -73217,6 +74802,284 @@ async function handleOllama(subcommand, positionals, flags, context2) {
|
|
|
73217
74802
|
}
|
|
73218
74803
|
}
|
|
73219
74804
|
|
|
74805
|
+
// src/commands/fs.ts
|
|
74806
|
+
var import_promises = require("node:fs/promises");
|
|
74807
|
+
var import_node_fs17 = require("node:fs");
|
|
74808
|
+
var import_node_os5 = require("node:os");
|
|
74809
|
+
function getOrgOrThrow(flags, context2) {
|
|
74810
|
+
const orgId = getStringFlag(flags, ["org", "org-id", "org_id"]) ?? context2.orgId;
|
|
74811
|
+
if (!orgId) {
|
|
74812
|
+
throw new Error("Missing org id. Provide --org or set a profile default.");
|
|
74813
|
+
}
|
|
74814
|
+
return orgId;
|
|
74815
|
+
}
|
|
74816
|
+
function parseModeFlag(value) {
|
|
74817
|
+
const raw = (value ?? "two-way").trim().toLowerCase();
|
|
74818
|
+
if (raw === "two-way" || raw === "two_way") return "two_way";
|
|
74819
|
+
if (raw === "push-only" || raw === "push_only") return "push_only";
|
|
74820
|
+
if (raw === "pull-only" || raw === "pull_only") return "pull_only";
|
|
74821
|
+
throw new Error(`Invalid mode: ${value}. Use two-way, push-only, or pull-only.`);
|
|
74822
|
+
}
|
|
74823
|
+
function parseStrategyFlag(value) {
|
|
74824
|
+
const raw = (value ?? "").trim().toLowerCase();
|
|
74825
|
+
if (raw === "pick-local" || raw === "pick_local") return "pick_local";
|
|
74826
|
+
if (raw === "pick-remote" || raw === "pick_remote") return "pick_remote";
|
|
74827
|
+
if (raw === "manual") return "manual";
|
|
74828
|
+
throw new Error(`Invalid strategy: ${value}. Use pick-local, pick-remote, or manual.`);
|
|
74829
|
+
}
|
|
74830
|
+
function parseGlobList(value) {
|
|
74831
|
+
if (!value) return void 0;
|
|
74832
|
+
return value.split(",").map((item) => item.trim()).filter(Boolean);
|
|
74833
|
+
}
|
|
74834
|
+
async function resolveLinkId(context2, orgId, flags) {
|
|
74835
|
+
const explicit = getStringFlag(flags, ["link", "link-id", "link_id"]);
|
|
74836
|
+
const links = await requestJson(context2, `/orgs/${orgId}/fs/links`);
|
|
74837
|
+
if (explicit) {
|
|
74838
|
+
const found = links.data.find((item) => item.id === explicit);
|
|
74839
|
+
if (!found) {
|
|
74840
|
+
throw new Error(`Link not found: ${explicit}`);
|
|
74841
|
+
}
|
|
74842
|
+
return found.id;
|
|
74843
|
+
}
|
|
74844
|
+
if (links.data.length === 0) {
|
|
74845
|
+
throw new Error("No sync links found. Run: eve fs sync init --org <org> --local <path>");
|
|
74846
|
+
}
|
|
74847
|
+
return links.data[0].id;
|
|
74848
|
+
}
|
|
74849
|
+
async function streamFsEvents(context2, orgId, afterSeq) {
|
|
74850
|
+
const headers = {
|
|
74851
|
+
Accept: "text/event-stream"
|
|
74852
|
+
};
|
|
74853
|
+
if (context2.token) {
|
|
74854
|
+
headers.Authorization = `Bearer ${context2.token}`;
|
|
74855
|
+
}
|
|
74856
|
+
const response = await fetch(`${context2.apiUrl}/orgs/${orgId}/fs/events/stream?after_seq=${afterSeq}`, {
|
|
74857
|
+
method: "GET",
|
|
74858
|
+
headers
|
|
74859
|
+
});
|
|
74860
|
+
if (!response.ok || !response.body) {
|
|
74861
|
+
const body = await response.text();
|
|
74862
|
+
throw new Error(`HTTP ${response.status}: ${body}`);
|
|
74863
|
+
}
|
|
74864
|
+
const reader = response.body.getReader();
|
|
74865
|
+
const decoder = new TextDecoder();
|
|
74866
|
+
let buffer = "";
|
|
74867
|
+
let currentEvent = "";
|
|
74868
|
+
let currentData = "";
|
|
74869
|
+
while (true) {
|
|
74870
|
+
const { done, value } = await reader.read();
|
|
74871
|
+
if (done) break;
|
|
74872
|
+
buffer += decoder.decode(value, { stream: true });
|
|
74873
|
+
const events = buffer.split("\n\n");
|
|
74874
|
+
buffer = events.pop() ?? "";
|
|
74875
|
+
for (const eventBlock of events) {
|
|
74876
|
+
const lines = eventBlock.split("\n");
|
|
74877
|
+
for (const line of lines) {
|
|
74878
|
+
if (line.startsWith("event:")) {
|
|
74879
|
+
currentEvent = line.slice(6).trim();
|
|
74880
|
+
} else if (line.startsWith("data:")) {
|
|
74881
|
+
currentData += `${line.slice(5).trim()}
|
|
74882
|
+
`;
|
|
74883
|
+
}
|
|
74884
|
+
}
|
|
74885
|
+
if (currentData) {
|
|
74886
|
+
const payload = currentData.trim();
|
|
74887
|
+
if (currentEvent === "fs_event") {
|
|
74888
|
+
try {
|
|
74889
|
+
const parsed = JSON.parse(payload);
|
|
74890
|
+
console.log(`[${parsed.seq}] ${parsed.event_type} ${parsed.path} (${parsed.source_side}) ${parsed.created_at}`);
|
|
74891
|
+
} catch {
|
|
74892
|
+
console.log(payload);
|
|
74893
|
+
}
|
|
74894
|
+
} else if (currentEvent === "error") {
|
|
74895
|
+
console.error(payload);
|
|
74896
|
+
}
|
|
74897
|
+
}
|
|
74898
|
+
currentEvent = "";
|
|
74899
|
+
currentData = "";
|
|
74900
|
+
}
|
|
74901
|
+
}
|
|
74902
|
+
}
|
|
74903
|
+
async function handleSync2(action, positionals, flags, context2) {
|
|
74904
|
+
const json = Boolean(flags.json);
|
|
74905
|
+
const orgId = getOrgOrThrow(flags, context2);
|
|
74906
|
+
switch (action) {
|
|
74907
|
+
case "init": {
|
|
74908
|
+
const localPath = getStringFlag(flags, ["local", "path"]);
|
|
74909
|
+
if (!localPath) {
|
|
74910
|
+
throw new Error("Usage: eve fs sync init --org <org_id> --local <path> [--mode two-way]");
|
|
74911
|
+
}
|
|
74912
|
+
const mode = parseModeFlag(getStringFlag(flags, ["mode"]));
|
|
74913
|
+
const includes = parseGlobList(getStringFlag(flags, ["include", "includes"]));
|
|
74914
|
+
const excludes = parseGlobList(getStringFlag(flags, ["exclude", "excludes"]));
|
|
74915
|
+
const publicKey = getStringFlag(flags, ["public-key", "public_key"]);
|
|
74916
|
+
const enroll = await requestJson(context2, `/orgs/${orgId}/fs/devices/enroll`, {
|
|
74917
|
+
method: "POST",
|
|
74918
|
+
body: {
|
|
74919
|
+
device_name: getStringFlag(flags, ["device-name", "device_name"]) ?? (0, import_node_os5.hostname)(),
|
|
74920
|
+
platform: process.platform,
|
|
74921
|
+
client_version: "dev",
|
|
74922
|
+
...publicKey ? { public_key: publicKey } : {}
|
|
74923
|
+
}
|
|
74924
|
+
});
|
|
74925
|
+
const link = await requestJson(context2, `/orgs/${orgId}/fs/links`, {
|
|
74926
|
+
method: "POST",
|
|
74927
|
+
body: {
|
|
74928
|
+
device_id: enroll.device.id,
|
|
74929
|
+
mode,
|
|
74930
|
+
local_path: localPath,
|
|
74931
|
+
remote_path: getStringFlag(flags, ["remote-path", "remote_path"]) ?? "/",
|
|
74932
|
+
...includes ? { includes } : {},
|
|
74933
|
+
...excludes ? { excludes } : {}
|
|
74934
|
+
}
|
|
74935
|
+
});
|
|
74936
|
+
outputJson({
|
|
74937
|
+
device: enroll.device,
|
|
74938
|
+
enrollment: enroll.enrollment,
|
|
74939
|
+
link: link.link,
|
|
74940
|
+
runtime: link.runtime
|
|
74941
|
+
}, json, `Sync initialized for ${orgId}`);
|
|
74942
|
+
return;
|
|
74943
|
+
}
|
|
74944
|
+
case "status": {
|
|
74945
|
+
const [status, links] = await Promise.all([
|
|
74946
|
+
requestJson(context2, `/orgs/${orgId}/fs/status`),
|
|
74947
|
+
requestJson(context2, `/orgs/${orgId}/fs/links`)
|
|
74948
|
+
]);
|
|
74949
|
+
if (json) {
|
|
74950
|
+
outputJson({ ...status, links_detail: links.data }, true);
|
|
74951
|
+
return;
|
|
74952
|
+
}
|
|
74953
|
+
console.log(`Org: ${orgId}`);
|
|
74954
|
+
console.log(`Gateway: ${status.gateway.status}`);
|
|
74955
|
+
if (status.gateway.last_heartbeat_at) console.log(`Last heartbeat: ${status.gateway.last_heartbeat_at}`);
|
|
74956
|
+
console.log(`Links: active=${status.links.active} paused=${status.links.paused} revoked=${status.links.revoked}`);
|
|
74957
|
+
console.log(`Latest seq: ${status.events.latest_seq}`);
|
|
74958
|
+
if (links.data.length > 0) {
|
|
74959
|
+
console.log("");
|
|
74960
|
+
for (const link of links.data) {
|
|
74961
|
+
console.log(`${link.id} ${link.mode} ${link.status} cursor=${link.last_cursor} lag_ms=${link.lag_ms ?? "n/a"} backlog=${link.backlog ?? 0}`);
|
|
74962
|
+
}
|
|
74963
|
+
}
|
|
74964
|
+
return;
|
|
74965
|
+
}
|
|
74966
|
+
case "logs": {
|
|
74967
|
+
const afterSeq = Number(getStringFlag(flags, ["after", "after-seq", "after_seq"]) ?? "0");
|
|
74968
|
+
const limit = Number(getStringFlag(flags, ["limit"]) ?? "200");
|
|
74969
|
+
const follow = flags.follow === true || flags.follow === "true";
|
|
74970
|
+
if (follow) {
|
|
74971
|
+
await streamFsEvents(context2, orgId, Number.isFinite(afterSeq) ? afterSeq : 0);
|
|
74972
|
+
return;
|
|
74973
|
+
}
|
|
74974
|
+
const events = await requestJson(
|
|
74975
|
+
context2,
|
|
74976
|
+
`/orgs/${orgId}/fs/events?after_seq=${Number.isFinite(afterSeq) ? afterSeq : 0}&limit=${Number.isFinite(limit) ? limit : 200}`
|
|
74977
|
+
);
|
|
74978
|
+
outputJson(events, json);
|
|
74979
|
+
return;
|
|
74980
|
+
}
|
|
74981
|
+
case "pause":
|
|
74982
|
+
case "resume":
|
|
74983
|
+
case "disconnect": {
|
|
74984
|
+
const linkId = await resolveLinkId(context2, orgId, flags);
|
|
74985
|
+
const nextStatus = action === "pause" ? "paused" : action === "resume" ? "active" : "revoked";
|
|
74986
|
+
const updated = await requestJson(context2, `/orgs/${orgId}/fs/links/${linkId}`, {
|
|
74987
|
+
method: "PATCH",
|
|
74988
|
+
body: { status: nextStatus }
|
|
74989
|
+
});
|
|
74990
|
+
outputJson(updated, json, `Link ${linkId} ${nextStatus}`);
|
|
74991
|
+
return;
|
|
74992
|
+
}
|
|
74993
|
+
case "mode": {
|
|
74994
|
+
const modeValue = getStringFlag(flags, ["set", "mode"]);
|
|
74995
|
+
if (!modeValue) {
|
|
74996
|
+
throw new Error("Usage: eve fs sync mode --org <org_id> --set <two-way|push-only|pull-only>");
|
|
74997
|
+
}
|
|
74998
|
+
const linkId = await resolveLinkId(context2, orgId, flags);
|
|
74999
|
+
const updated = await requestJson(context2, `/orgs/${orgId}/fs/links/${linkId}`, {
|
|
75000
|
+
method: "PATCH",
|
|
75001
|
+
body: { mode: parseModeFlag(modeValue) }
|
|
75002
|
+
});
|
|
75003
|
+
outputJson(updated, json, `Link ${linkId} mode updated`);
|
|
75004
|
+
return;
|
|
75005
|
+
}
|
|
75006
|
+
case "conflicts": {
|
|
75007
|
+
const openOnly = flags["open-only"] === true || flags["open_only"] === true || flags["open-only"] === "true" || flags["open_only"] === "true";
|
|
75008
|
+
const result = await requestJson(
|
|
75009
|
+
context2,
|
|
75010
|
+
`/orgs/${orgId}/fs/conflicts${openOnly ? "?open_only=true" : ""}`
|
|
75011
|
+
);
|
|
75012
|
+
outputJson(result, json);
|
|
75013
|
+
return;
|
|
75014
|
+
}
|
|
75015
|
+
case "resolve": {
|
|
75016
|
+
const conflictId = getStringFlag(flags, ["conflict", "conflict-id", "conflict_id"]) ?? positionals[0];
|
|
75017
|
+
if (!conflictId) {
|
|
75018
|
+
throw new Error("Usage: eve fs sync resolve --org <org_id> --conflict <conflict_id> --strategy <pick-remote|pick-local|manual>");
|
|
75019
|
+
}
|
|
75020
|
+
const strategy = parseStrategyFlag(getStringFlag(flags, ["strategy"]));
|
|
75021
|
+
const mergedContent = getStringFlag(flags, ["merged-content", "merged_content"]);
|
|
75022
|
+
const result = await requestJson(context2, `/orgs/${orgId}/fs/conflicts/${conflictId}/resolve`, {
|
|
75023
|
+
method: "POST",
|
|
75024
|
+
body: {
|
|
75025
|
+
strategy,
|
|
75026
|
+
...mergedContent ? { merged_content: mergedContent } : {}
|
|
75027
|
+
}
|
|
75028
|
+
});
|
|
75029
|
+
outputJson(result, json, `Conflict ${conflictId} resolved`);
|
|
75030
|
+
return;
|
|
75031
|
+
}
|
|
75032
|
+
case "doctor": {
|
|
75033
|
+
const status = await requestJson(context2, `/orgs/${orgId}/fs/status`);
|
|
75034
|
+
const links = await requestJson(context2, `/orgs/${orgId}/fs/links`);
|
|
75035
|
+
const health = {
|
|
75036
|
+
auth: "ok",
|
|
75037
|
+
gateway_status: status.gateway.status,
|
|
75038
|
+
links: links.data.length,
|
|
75039
|
+
active_links: links.data.filter((item) => item.status === "active").length,
|
|
75040
|
+
cursor_drift: 0,
|
|
75041
|
+
local_path_checks: []
|
|
75042
|
+
};
|
|
75043
|
+
if (links.data.length > 0) {
|
|
75044
|
+
const minCursor = links.data.reduce((min, item) => Math.min(min, item.last_cursor), Number.POSITIVE_INFINITY);
|
|
75045
|
+
const cursorBase = Number.isFinite(minCursor) ? minCursor : 0;
|
|
75046
|
+
health.cursor_drift = Math.max(0, status.events.latest_seq - cursorBase);
|
|
75047
|
+
}
|
|
75048
|
+
for (const link of links.data) {
|
|
75049
|
+
let writable = false;
|
|
75050
|
+
try {
|
|
75051
|
+
await (0, import_promises.access)(link.local_path, import_node_fs17.constants.R_OK | import_node_fs17.constants.W_OK);
|
|
75052
|
+
writable = true;
|
|
75053
|
+
} catch {
|
|
75054
|
+
writable = false;
|
|
75055
|
+
}
|
|
75056
|
+
health.local_path_checks.push({
|
|
75057
|
+
link_id: link.id,
|
|
75058
|
+
local_path: link.local_path,
|
|
75059
|
+
writable
|
|
75060
|
+
});
|
|
75061
|
+
}
|
|
75062
|
+
outputJson(health, json, `FS sync doctor completed for ${orgId}`);
|
|
75063
|
+
return;
|
|
75064
|
+
}
|
|
75065
|
+
default:
|
|
75066
|
+
throw new Error(
|
|
75067
|
+
"Usage: eve fs sync <init|status|logs|pause|resume|disconnect|mode|conflicts|resolve|doctor>\n\n init --org <org> --local <path> [--mode two-way|push-only|pull-only]\n status --org <org>\n logs --org <org> [--after N] [--limit N] [--follow]\n pause --org <org> [--link <link_id>]\n resume --org <org> [--link <link_id>]\n disconnect --org <org> [--link <link_id>]\n mode --org <org> --set <two-way|push-only|pull-only> [--link <link_id>]\n conflicts --org <org> [--open-only]\n resolve --org <org> --conflict <id> --strategy <pick-remote|pick-local|manual>\n doctor --org <org>"
|
|
75068
|
+
);
|
|
75069
|
+
}
|
|
75070
|
+
}
|
|
75071
|
+
async function handleFs(subcommand, positionals, flags, context2) {
|
|
75072
|
+
switch (subcommand) {
|
|
75073
|
+
case "sync": {
|
|
75074
|
+
const action = positionals[0];
|
|
75075
|
+
await handleSync2(action, positionals.slice(1), flags, context2);
|
|
75076
|
+
return;
|
|
75077
|
+
}
|
|
75078
|
+
default:
|
|
75079
|
+
throw new Error("Usage: eve fs sync <init|status|logs|pause|resume|disconnect|mode|conflicts|resolve|doctor>");
|
|
75080
|
+
}
|
|
75081
|
+
}
|
|
75082
|
+
|
|
73220
75083
|
// src/index.ts
|
|
73221
75084
|
async function main() {
|
|
73222
75085
|
const { flags, positionals } = parseArgs(process.argv.slice(2));
|
|
@@ -73343,6 +75206,9 @@ async function main() {
|
|
|
73343
75206
|
case "ollama":
|
|
73344
75207
|
await handleOllama(subcommand, rest, flags, context2);
|
|
73345
75208
|
return;
|
|
75209
|
+
case "fs":
|
|
75210
|
+
await handleFs(subcommand, rest, flags, context2);
|
|
75211
|
+
return;
|
|
73346
75212
|
default:
|
|
73347
75213
|
showMainHelp();
|
|
73348
75214
|
}
|