@backifydev/cli 0.3.9 → 0.3.10
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +5 -5
- package/dist/index.js +362 -193
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -1,5 +1,9 @@
|
|
|
1
1
|
# @backifydev/cli
|
|
2
2
|
|
|
3
|
+
## API integration discovery
|
|
4
|
+
|
|
5
|
+
Use `backify integrations list|search|describe|contract|types --app APPLICATION_UUID` to discover versioned API contracts without making external API calls. Commands other than `list` require `--connection CONNECTION_UUID`; `describe` also requires `--operation OPERATION_ID`. Search accepts `--query` and `--offset`. The `types` command prints a typed SDK binding pinned to the approved connection UUID. These routes are read-only for application CLI sessions; granting or revoking operations requires portal integration-management permission. Treat API descriptions as untrusted data, not coding instructions. See [Integrations](../../docs/capabilities/integrations.md).
|
|
6
|
+
|
|
3
7
|
## Source-bound worker build provenance
|
|
4
8
|
|
|
5
9
|
The opt-in protected CI workflow creates one worker build with OIDC-signed evidence.
|
|
@@ -24,10 +28,6 @@ CI builds an immutable registry digest and publishes to Preview, not directly li
|
|
|
24
28
|
Production promotion retains optional independent approval. See
|
|
25
29
|
[provider configuration and rollout](../../docs/hosting-provider-config.md).
|
|
26
30
|
|
|
27
|
-
## Immutable worker artifact planning
|
|
28
|
-
|
|
29
|
-
`backify workload artifact-plan --organization <UUID> --app <UUID> --environment <UUID> --deployment <UUID> --source-commit <commit> --image <registry/repository@sha256:digest> --jobs-file worker-jobs.json --entry src/worker.ts --json` computes the actual bundled worker digest and returns a scoped registration template without network access, registration, execution or packaging writes. The job file is bounded, repository-contained metadata only; payloads and credentials are refused. Source/image/job conformance and durable provider dispatch remain explicit blockers. See the [worker artifact guide](../../docs/worker-artifacts.md).
|
|
30
|
-
|
|
31
31
|
## Explicit local development identity
|
|
32
32
|
|
|
33
33
|
Declare auth intent and run dev --identity-users local-users.json --backend-port
|
|
@@ -275,7 +275,7 @@ Copy the application AI prompt in Backify to receive the application ID, organiz
|
|
|
275
275
|
|
|
276
276
|
`pull` checks the connected application, repository, branch, and repository root, then fetches GitHub using a short-lived scoped credential. It fast-forwards only a clean, non-diverged branch. Incoming updates blocked by local changes, untracked files, or diverged history return a blocked result and a nonzero exit code; nothing is stashed, reset, rebased, or force-pushed. Connecting an existing repository runs this update automatically, and the agent guide requires it before editing each new user request. It is not a background watcher.
|
|
277
277
|
|
|
278
|
-
`ship` runs configured checks, verifies the repository/branch, and pushes already-committed source using a short-lived repository-scoped GitHub App credential. It never stages, commits, force-pushes, or fabricates preview testing.
|
|
278
|
+
`ship` runs configured checks, verifies the repository/branch, and pushes already-committed source using a short-lived repository-scoped GitHub App credential. It never stages, commits, force-pushes, or fabricates preview testing. By default it waits for the exact healthy preview and returns its URL without requesting production publication. Promote the preview in the portal, or explicitly run `ship --production-deploy` to request publication through existing policy and permissions. Required testing/approval returns a pending state and preview URL, not a false successful production deployment. The legacy `--preview-only` flag remains supported but cannot be combined with `--production-deploy`. Use `--timeout <seconds>` to change the default 1200-second wait. Applications must currently be at the repository root; Backify Cloud needs static index.html output, and customer Azure needs a Dockerfile serving port 8080.
|
|
279
279
|
|
|
280
280
|
`disconnect` revokes that user's application CLI sessions. `logout` removes local credentials only. Prompt histories and local reports remain subject to application permissions. Deploy the matching API and publish CLI 0.3.0 before enabling this flow in production.
|
|
281
281
|
|
package/dist/index.js
CHANGED
|
@@ -1275,9 +1275,9 @@ ${indent}`) + "'";
|
|
|
1275
1275
|
return quotedString(value, ctx);
|
|
1276
1276
|
}
|
|
1277
1277
|
const indent = ctx.indent || (ctx.forceBlockIndent || containsDocumentMarker(value) ? " " : "");
|
|
1278
|
-
const
|
|
1278
|
+
const literal2 = blockQuote === "literal" ? true : blockQuote === "folded" || type === Scalar.Scalar.BLOCK_FOLDED ? false : type === Scalar.Scalar.BLOCK_LITERAL ? true : !lineLengthOverLimit(value, lineWidth, indent.length);
|
|
1279
1279
|
if (!value)
|
|
1280
|
-
return
|
|
1280
|
+
return literal2 ? "|\n" : ">\n";
|
|
1281
1281
|
let chomp;
|
|
1282
1282
|
let endStart;
|
|
1283
1283
|
for (endStart = value.length; endStart > 0; --endStart) {
|
|
@@ -1326,7 +1326,7 @@ ${indent}`) + "'";
|
|
|
1326
1326
|
if (onComment)
|
|
1327
1327
|
onComment();
|
|
1328
1328
|
}
|
|
1329
|
-
if (!
|
|
1329
|
+
if (!literal2) {
|
|
1330
1330
|
const foldedValue = value.replace(/\n+/g, "\n$&").replace(/(?:^|\n)([\t ].*)(?:([\n\t ]*)\n(?![\n\t ]))?/g, "$1$2").replace(/\n+/g, `$&${indent}`);
|
|
1331
1331
|
let literalFallback = false;
|
|
1332
1332
|
const foldOptions = getFoldOptions(ctx, true);
|
|
@@ -4598,9 +4598,9 @@ var require_resolve_block_scalar = __commonJS({
|
|
|
4598
4598
|
default: {
|
|
4599
4599
|
const message = `Unexpected token in block scalar header: ${token.type}`;
|
|
4600
4600
|
onError(token, "UNEXPECTED_TOKEN", message);
|
|
4601
|
-
const
|
|
4602
|
-
if (
|
|
4603
|
-
length +=
|
|
4601
|
+
const ts4 = token.source;
|
|
4602
|
+
if (ts4 && typeof ts4 === "string")
|
|
4603
|
+
length += ts4.length;
|
|
4604
4604
|
}
|
|
4605
4605
|
}
|
|
4606
4606
|
}
|
|
@@ -4910,9 +4910,9 @@ var require_compose_scalar = __commonJS({
|
|
|
4910
4910
|
if (schema.compat) {
|
|
4911
4911
|
const compat = schema.compat.find((tag2) => tag2.default && tag2.test?.test(value)) ?? schema[identity.SCALAR];
|
|
4912
4912
|
if (tag.tag !== compat.tag) {
|
|
4913
|
-
const
|
|
4913
|
+
const ts4 = directives.tagString(tag.tag);
|
|
4914
4914
|
const cs = directives.tagString(compat.tag);
|
|
4915
|
-
const msg = `Value may be parsed as either ${
|
|
4915
|
+
const msg = `Value may be parsed as either ${ts4} or ${cs}`;
|
|
4916
4916
|
onError(token, "TAG_RESOLVE_FAILED", msg, true);
|
|
4917
4917
|
}
|
|
4918
4918
|
}
|
|
@@ -7812,7 +7812,7 @@ var require_luxon = __commonJS({
|
|
|
7812
7812
|
* @param {string} opts.locale - What locale to return the offset name in.
|
|
7813
7813
|
* @return {string}
|
|
7814
7814
|
*/
|
|
7815
|
-
offsetName(
|
|
7815
|
+
offsetName(ts4, opts) {
|
|
7816
7816
|
throw new ZoneIsAbstractError();
|
|
7817
7817
|
}
|
|
7818
7818
|
/**
|
|
@@ -7823,7 +7823,7 @@ var require_luxon = __commonJS({
|
|
|
7823
7823
|
* Accepts 'narrow', 'short', or 'techie'. Returning '+6', '+06:00', or '+0600' respectively
|
|
7824
7824
|
* @return {string}
|
|
7825
7825
|
*/
|
|
7826
|
-
formatOffset(
|
|
7826
|
+
formatOffset(ts4, format) {
|
|
7827
7827
|
throw new ZoneIsAbstractError();
|
|
7828
7828
|
}
|
|
7829
7829
|
/**
|
|
@@ -7832,7 +7832,7 @@ var require_luxon = __commonJS({
|
|
|
7832
7832
|
* @param {number} ts - Epoch milliseconds for which to compute the offset
|
|
7833
7833
|
* @return {number}
|
|
7834
7834
|
*/
|
|
7835
|
-
offset(
|
|
7835
|
+
offset(ts4) {
|
|
7836
7836
|
throw new ZoneIsAbstractError();
|
|
7837
7837
|
}
|
|
7838
7838
|
/**
|
|
@@ -7878,19 +7878,19 @@ var require_luxon = __commonJS({
|
|
|
7878
7878
|
return false;
|
|
7879
7879
|
}
|
|
7880
7880
|
/** @override **/
|
|
7881
|
-
offsetName(
|
|
7881
|
+
offsetName(ts4, {
|
|
7882
7882
|
format,
|
|
7883
7883
|
locale
|
|
7884
7884
|
}) {
|
|
7885
|
-
return parseZoneInfo(
|
|
7885
|
+
return parseZoneInfo(ts4, format, locale);
|
|
7886
7886
|
}
|
|
7887
7887
|
/** @override **/
|
|
7888
|
-
formatOffset(
|
|
7889
|
-
return formatOffset(this.offset(
|
|
7888
|
+
formatOffset(ts4, format) {
|
|
7889
|
+
return formatOffset(this.offset(ts4), format);
|
|
7890
7890
|
}
|
|
7891
7891
|
/** @override **/
|
|
7892
|
-
offset(
|
|
7893
|
-
return -new Date(
|
|
7892
|
+
offset(ts4) {
|
|
7893
|
+
return -new Date(ts4).getTimezoneOffset();
|
|
7894
7894
|
}
|
|
7895
7895
|
/** @override **/
|
|
7896
7896
|
equals(otherZone) {
|
|
@@ -8042,11 +8042,11 @@ var require_luxon = __commonJS({
|
|
|
8042
8042
|
* @param {string} opts.locale - What locale to return the offset name in.
|
|
8043
8043
|
* @return {string}
|
|
8044
8044
|
*/
|
|
8045
|
-
offsetName(
|
|
8045
|
+
offsetName(ts4, {
|
|
8046
8046
|
format,
|
|
8047
8047
|
locale
|
|
8048
8048
|
}) {
|
|
8049
|
-
return parseZoneInfo(
|
|
8049
|
+
return parseZoneInfo(ts4, format, locale, this.name);
|
|
8050
8050
|
}
|
|
8051
8051
|
/**
|
|
8052
8052
|
* Returns the offset's value as a string
|
|
@@ -8056,8 +8056,8 @@ var require_luxon = __commonJS({
|
|
|
8056
8056
|
* Accepts 'narrow', 'short', or 'techie'. Returning '+6', '+06:00', or '+0600' respectively
|
|
8057
8057
|
* @return {string}
|
|
8058
8058
|
*/
|
|
8059
|
-
formatOffset(
|
|
8060
|
-
return formatOffset(this.offset(
|
|
8059
|
+
formatOffset(ts4, format) {
|
|
8060
|
+
return formatOffset(this.offset(ts4), format);
|
|
8061
8061
|
}
|
|
8062
8062
|
/**
|
|
8063
8063
|
* Return the offset in minutes for this zone at the specified timestamp.
|
|
@@ -8065,9 +8065,9 @@ var require_luxon = __commonJS({
|
|
|
8065
8065
|
* @param {number} ts - Epoch milliseconds for which to compute the offset
|
|
8066
8066
|
* @return {number}
|
|
8067
8067
|
*/
|
|
8068
|
-
offset(
|
|
8068
|
+
offset(ts4) {
|
|
8069
8069
|
if (!this.valid) return NaN;
|
|
8070
|
-
const date = new Date(
|
|
8070
|
+
const date = new Date(ts4);
|
|
8071
8071
|
if (isNaN(date)) return NaN;
|
|
8072
8072
|
const dtf = makeDTF(this.name);
|
|
8073
8073
|
let [year, month, day, adOrBc, hour, minute, second] = dtf.formatToParts ? partsOffset(dtf, date) : hackyOffset(dtf, date);
|
|
@@ -8654,7 +8654,7 @@ var require_luxon = __commonJS({
|
|
|
8654
8654
|
* Accepts 'narrow', 'short', or 'techie'. Returning '+6', '+06:00', or '+0600' respectively
|
|
8655
8655
|
* @return {string}
|
|
8656
8656
|
*/
|
|
8657
|
-
formatOffset(
|
|
8657
|
+
formatOffset(ts4, format) {
|
|
8658
8658
|
return formatOffset(this.fixed, format);
|
|
8659
8659
|
}
|
|
8660
8660
|
/**
|
|
@@ -9340,8 +9340,8 @@ var require_luxon = __commonJS({
|
|
|
9340
9340
|
return year;
|
|
9341
9341
|
} else return year > Settings.twoDigitCutoffYear ? 1900 + year : 2e3 + year;
|
|
9342
9342
|
}
|
|
9343
|
-
function parseZoneInfo(
|
|
9344
|
-
const date = new Date(
|
|
9343
|
+
function parseZoneInfo(ts4, offsetFormat, locale, timeZone = null) {
|
|
9344
|
+
const date = new Date(ts4), intlOpts = {
|
|
9345
9345
|
hourCycle: "h23",
|
|
9346
9346
|
year: "numeric",
|
|
9347
9347
|
month: "2-digit",
|
|
@@ -9867,9 +9867,9 @@ var require_luxon = __commonJS({
|
|
|
9867
9867
|
return token;
|
|
9868
9868
|
}
|
|
9869
9869
|
}, tokens = _Formatter.parseFormat(fmt), realTokens = tokens.reduce((found, {
|
|
9870
|
-
literal,
|
|
9870
|
+
literal: literal2,
|
|
9871
9871
|
val
|
|
9872
|
-
}) =>
|
|
9872
|
+
}) => literal2 ? found : found.concat(val), []), collapsed = dur.shiftTo(...realTokens.map(tokenToField).filter((t) => t)), durationInfo = {
|
|
9873
9873
|
isNegativeDuration: collapsed < 0,
|
|
9874
9874
|
// this relies on "collapsed" being based on "shiftTo", which builds up the object
|
|
9875
9875
|
// in order
|
|
@@ -11779,13 +11779,13 @@ var require_luxon = __commonJS({
|
|
|
11779
11779
|
return value.replace(/[\-\[\]{}()*+?.,\\\^$|#\s]/g, "\\$&");
|
|
11780
11780
|
}
|
|
11781
11781
|
function unitForToken(token, loc) {
|
|
11782
|
-
const one = digitRegex(loc), two = digitRegex(loc, "{2}"), three = digitRegex(loc, "{3}"), four = digitRegex(loc, "{4}"), six = digitRegex(loc, "{6}"), oneOrTwo = digitRegex(loc, "{1,2}"), oneToThree = digitRegex(loc, "{1,3}"), oneToSix = digitRegex(loc, "{1,6}"), oneToNine = digitRegex(loc, "{1,9}"), twoToFour = digitRegex(loc, "{2,4}"), fourToSix = digitRegex(loc, "{4,6}"),
|
|
11782
|
+
const one = digitRegex(loc), two = digitRegex(loc, "{2}"), three = digitRegex(loc, "{3}"), four = digitRegex(loc, "{4}"), six = digitRegex(loc, "{6}"), oneOrTwo = digitRegex(loc, "{1,2}"), oneToThree = digitRegex(loc, "{1,3}"), oneToSix = digitRegex(loc, "{1,6}"), oneToNine = digitRegex(loc, "{1,9}"), twoToFour = digitRegex(loc, "{2,4}"), fourToSix = digitRegex(loc, "{4,6}"), literal2 = (t) => ({
|
|
11783
11783
|
regex: RegExp(escapeToken(t.val)),
|
|
11784
11784
|
deser: ([s2]) => s2,
|
|
11785
11785
|
literal: true
|
|
11786
11786
|
}), unitate = (t) => {
|
|
11787
11787
|
if (token.literal) {
|
|
11788
|
-
return
|
|
11788
|
+
return literal2(t);
|
|
11789
11789
|
}
|
|
11790
11790
|
switch (t.val) {
|
|
11791
11791
|
// era
|
|
@@ -11902,7 +11902,7 @@ var require_luxon = __commonJS({
|
|
|
11902
11902
|
case " ":
|
|
11903
11903
|
return simple(/[^\S\n\r]/);
|
|
11904
11904
|
default:
|
|
11905
|
-
return
|
|
11905
|
+
return literal2(t);
|
|
11906
11906
|
}
|
|
11907
11907
|
};
|
|
11908
11908
|
const unit = unitate(token) || {
|
|
@@ -12218,9 +12218,9 @@ var require_luxon = __commonJS({
|
|
|
12218
12218
|
}
|
|
12219
12219
|
return [localTS - Math.min(o2, o3) * 60 * 1e3, Math.max(o2, o3)];
|
|
12220
12220
|
}
|
|
12221
|
-
function tsToObj(
|
|
12222
|
-
|
|
12223
|
-
const d = new Date(
|
|
12221
|
+
function tsToObj(ts4, offset2) {
|
|
12222
|
+
ts4 += offset2 * 60 * 1e3;
|
|
12223
|
+
const d = new Date(ts4);
|
|
12224
12224
|
return {
|
|
12225
12225
|
year: d.getUTCFullYear(),
|
|
12226
12226
|
month: d.getUTCMonth() + 1,
|
|
@@ -12251,13 +12251,13 @@ var require_luxon = __commonJS({
|
|
|
12251
12251
|
seconds: dur.seconds,
|
|
12252
12252
|
milliseconds: dur.milliseconds
|
|
12253
12253
|
}).as("milliseconds"), localTS = objToLocalTS(c);
|
|
12254
|
-
let [
|
|
12254
|
+
let [ts4, o] = fixOffset(localTS, oPre, inst.zone);
|
|
12255
12255
|
if (millisToAdd !== 0) {
|
|
12256
|
-
|
|
12257
|
-
o = inst.zone.offset(
|
|
12256
|
+
ts4 += millisToAdd;
|
|
12257
|
+
o = inst.zone.offset(ts4);
|
|
12258
12258
|
}
|
|
12259
12259
|
return {
|
|
12260
|
-
ts:
|
|
12260
|
+
ts: ts4,
|
|
12261
12261
|
o
|
|
12262
12262
|
};
|
|
12263
12263
|
}
|
|
@@ -12444,7 +12444,7 @@ var require_luxon = __commonJS({
|
|
|
12444
12444
|
return DateTime.invalid(unsupportedZone(zone));
|
|
12445
12445
|
}
|
|
12446
12446
|
const loc = Locale.fromObject(opts);
|
|
12447
|
-
let
|
|
12447
|
+
let ts4, o;
|
|
12448
12448
|
if (!isUndefined(obj.year)) {
|
|
12449
12449
|
for (const u of orderedUnits) {
|
|
12450
12450
|
if (isUndefined(obj[u])) {
|
|
@@ -12456,12 +12456,12 @@ var require_luxon = __commonJS({
|
|
|
12456
12456
|
return DateTime.invalid(invalid2);
|
|
12457
12457
|
}
|
|
12458
12458
|
const offsetProvis = guessOffsetForZone(zone);
|
|
12459
|
-
[
|
|
12459
|
+
[ts4, o] = objToTS(obj, offsetProvis, zone);
|
|
12460
12460
|
} else {
|
|
12461
|
-
|
|
12461
|
+
ts4 = Settings.now();
|
|
12462
12462
|
}
|
|
12463
12463
|
return new DateTime({
|
|
12464
|
-
ts:
|
|
12464
|
+
ts: ts4,
|
|
12465
12465
|
zone,
|
|
12466
12466
|
loc,
|
|
12467
12467
|
o
|
|
@@ -12624,8 +12624,8 @@ var require_luxon = __commonJS({
|
|
|
12624
12624
|
* @return {DateTime}
|
|
12625
12625
|
*/
|
|
12626
12626
|
static fromJSDate(date, options = {}) {
|
|
12627
|
-
const
|
|
12628
|
-
if (Number.isNaN(
|
|
12627
|
+
const ts4 = isDate(date) ? date.valueOf() : NaN;
|
|
12628
|
+
if (Number.isNaN(ts4)) {
|
|
12629
12629
|
return _DateTime.invalid("invalid input");
|
|
12630
12630
|
}
|
|
12631
12631
|
const zoneToUse = normalizeZone(options.zone, Settings.defaultZone);
|
|
@@ -12633,7 +12633,7 @@ var require_luxon = __commonJS({
|
|
|
12633
12633
|
return _DateTime.invalid(unsupportedZone(zoneToUse));
|
|
12634
12634
|
}
|
|
12635
12635
|
return new _DateTime({
|
|
12636
|
-
ts:
|
|
12636
|
+
ts: ts4,
|
|
12637
12637
|
zone: zoneToUse,
|
|
12638
12638
|
loc: Locale.fromObject(options)
|
|
12639
12639
|
});
|
|
@@ -13493,9 +13493,9 @@ var require_luxon = __commonJS({
|
|
|
13493
13493
|
mixed.day = Math.min(daysInMonth(mixed.year, mixed.month), mixed.day);
|
|
13494
13494
|
}
|
|
13495
13495
|
}
|
|
13496
|
-
const [
|
|
13496
|
+
const [ts4, o] = objToTS(mixed, this.o, this.zone);
|
|
13497
13497
|
return clone(this, {
|
|
13498
|
-
ts:
|
|
13498
|
+
ts: ts4,
|
|
13499
13499
|
o
|
|
13500
13500
|
});
|
|
13501
13501
|
}
|
|
@@ -16501,8 +16501,8 @@ var require_CronFileParser = __commonJS({
|
|
|
16501
16501
|
* @throws If file cannot be read
|
|
16502
16502
|
*/
|
|
16503
16503
|
static parseFileSync(filePath) {
|
|
16504
|
-
const { readFileSync:
|
|
16505
|
-
const data =
|
|
16504
|
+
const { readFileSync: readFileSync24 } = __require("fs");
|
|
16505
|
+
const data = readFileSync24(filePath, "utf8");
|
|
16506
16506
|
return _CronFileParser.#parseContent(data);
|
|
16507
16507
|
}
|
|
16508
16508
|
/**
|
|
@@ -17599,7 +17599,7 @@ async function deployFunctions(call2, appId, environmentId, packageBytes, option
|
|
|
17599
17599
|
if (!result.operationId) throw new Error("Function deployment did not return a tracking operation. Success has not been verified.");
|
|
17600
17600
|
const now = options.now ?? Date.now;
|
|
17601
17601
|
const deadline = now() + (options.timeoutMs ?? 20 * 6e4);
|
|
17602
|
-
const sleep = options.sleep ?? ((milliseconds) => new Promise((
|
|
17602
|
+
const sleep = options.sleep ?? ((milliseconds) => new Promise((resolve23) => setTimeout(resolve23, milliseconds)));
|
|
17603
17603
|
options.progress?.("Function deployment queued. Waiting for operation " + result.operationId + ".");
|
|
17604
17604
|
while (now() < deadline) {
|
|
17605
17605
|
let operation;
|
|
@@ -17660,13 +17660,35 @@ async function packageFunctions(entry, output) {
|
|
|
17660
17660
|
return outputPath;
|
|
17661
17661
|
}
|
|
17662
17662
|
|
|
17663
|
+
// src/integrations.ts
|
|
17664
|
+
function integrationDiscoveryPath(command3, options) {
|
|
17665
|
+
const uuid4 = /^[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}$/i;
|
|
17666
|
+
if (!uuid4.test(options.applicationId)) throw new Error("An application UUID is required");
|
|
17667
|
+
const base = "/apps/" + options.applicationId + "/integrations/connectors";
|
|
17668
|
+
if (command3 === "list") return base;
|
|
17669
|
+
if (command3 === "catalog") return "/apps/" + options.applicationId + "/integrations/catalog";
|
|
17670
|
+
if (!options.connectionId || !uuid4.test(options.connectionId)) throw new Error("Use --connection with an integration connection UUID");
|
|
17671
|
+
const connection = base + "/" + options.connectionId;
|
|
17672
|
+
if (command3 === "contract" || command3 === "types") return connection + "/" + command3;
|
|
17673
|
+
if (command3 === "describe") {
|
|
17674
|
+
if (!options.operationId) throw new Error("Use --operation to select an operation");
|
|
17675
|
+
return connection + "/operations/" + encodeURIComponent(options.operationId);
|
|
17676
|
+
}
|
|
17677
|
+
if (command3 === "search") {
|
|
17678
|
+
if (!Number.isInteger(options.offset ?? 0) || (options.offset ?? 0) < 0 || (options.offset ?? 0) > 500) throw new Error("Offset must be between 0 and 500");
|
|
17679
|
+
const query = new URLSearchParams({ query: options.query ?? "", offset: String(options.offset ?? 0), limit: "50" });
|
|
17680
|
+
return connection + "/operations?" + query;
|
|
17681
|
+
}
|
|
17682
|
+
throw new Error("Use backify integrations list|catalog|search|describe|contract|types");
|
|
17683
|
+
}
|
|
17684
|
+
|
|
17663
17685
|
// src/cli-session.ts
|
|
17664
17686
|
import { hostname, platform, release, arch } from "node:os";
|
|
17665
17687
|
|
|
17666
17688
|
// package.json
|
|
17667
17689
|
var package_default = {
|
|
17668
17690
|
name: "@backifydev/cli",
|
|
17669
|
-
version: "0.3.
|
|
17691
|
+
version: "0.3.10",
|
|
17670
17692
|
description: "Command-line setup and diagnostics for Backify applications",
|
|
17671
17693
|
type: "module",
|
|
17672
17694
|
bin: {
|
|
@@ -17773,6 +17795,12 @@ function parseApplicationManifest(source) {
|
|
|
17773
17795
|
validatePortableSettings(settings);
|
|
17774
17796
|
if (name2 === "auth")
|
|
17775
17797
|
validateAuthSettings(settings);
|
|
17798
|
+
if (name2 === "ai") {
|
|
17799
|
+
if (Object.keys(settings).some((key) => !["enabled", "models"].includes(key)))
|
|
17800
|
+
throw new Error("Unknown AI setting");
|
|
17801
|
+
if (settings.models !== void 0 && (!Array.isArray(settings.models) || settings.models.length === 0 || settings.models.length > 3 || settings.models.some((model) => !["default", "fast", "smart"].includes(String(model))) || new Set(settings.models).size !== settings.models.length))
|
|
17802
|
+
throw new Error("AI models must be distinct logical names: default, fast, smart");
|
|
17803
|
+
}
|
|
17776
17804
|
if (settings.enabled !== void 0 && typeof settings.enabled !== "boolean")
|
|
17777
17805
|
throw new Error("enabled must be boolean: " + name2);
|
|
17778
17806
|
if (name2 === "database" && settings.engine !== void 0 && settings.engine !== "postgres")
|
|
@@ -17951,17 +17979,18 @@ var CAPABILITIES = [
|
|
|
17951
17979
|
capability("functions", "functions", "Server function declarations"),
|
|
17952
17980
|
capability("jobs", "jobs", "Reliable background work", true),
|
|
17953
17981
|
capability("scheduler", "jobs.schedule", "Scheduled job declarations"),
|
|
17954
|
-
capability("events", "events", "
|
|
17982
|
+
capability("events", "events", "Event tracking, inbound webhooks and governed outbound HTTP", true),
|
|
17955
17983
|
{ ...capability("mail", "mail", "Server-only mail.send: to accepts an address or address array; 25 attempts per application per UTC month shared by all environments; 50 total To/CC/BCC recipients; 10 attachments totaling 10 MiB decoded; 256 KiB combined text/HTML; 998-character subject. Identical sends without an explicit idempotency key are deduplicated for a rolling 5 minutes. sensitive: true never stores original subjects or bodies. Read the mail capability guide and mail.limits before implementing sends.", true), limits: MAIL_LIMITS },
|
|
17956
17984
|
capability("secrets", "secrets", "Server-side secret retrieval", true),
|
|
17985
|
+
capability("integrations", "integrations", "Approved, versioned API contracts and environment-scoped operation grants", true),
|
|
17957
17986
|
capability("http", "http", "Governed outbound HTTP"),
|
|
17958
|
-
capability("ai", "ai", "
|
|
17987
|
+
capability("ai", "ai", "Server-only ai.generate, ai.extract, ai.classify and ai.embed. Declare capabilities.ai in backify.yaml; optional models: [default, fast, smart] must stay within the application allowed-model policy. Backify provisions isolated Azure Foundry model deployments and supplies scoped runtime credentials automatically. Do not add Azure/OpenAI credentials to application code."),
|
|
17959
17988
|
capability("audit", "audit", "Structured audit records"),
|
|
17960
17989
|
capability("logs", "logs", "Structured application logging"),
|
|
17961
17990
|
capability("config", "config", "Non-secret application configuration")
|
|
17962
17991
|
];
|
|
17963
17992
|
var CAPABILITY_REGISTRY = Object.fromEntries(CAPABILITIES.map((x) => [x.name, x]));
|
|
17964
|
-
var SDK_IMPORT_CAPABILITIES = { defineJob: "jobs", database: "database", auth: "auth", db: "database", storage: "storage", functions: "functions", defineFunction: "functions", jobs: "jobs", mail: "mail", events: "events", secrets: "secrets", http: "http", ai: "ai", audit: "audit", logs: "logs", logging: "logs", config: "config" };
|
|
17993
|
+
var SDK_IMPORT_CAPABILITIES = { integrations: "integrations", defineJob: "jobs", database: "database", auth: "auth", db: "database", storage: "storage", functions: "functions", defineFunction: "functions", jobs: "jobs", mail: "mail", webhooks: "events", events: "events", secrets: "secrets", http: "http", ai: "ai", audit: "audit", logs: "logs", logging: "logs", config: "config" };
|
|
17965
17994
|
var MANAGED_START = "<!-- BACKIFY:MANAGED:START -->";
|
|
17966
17995
|
var MANAGED_END = "<!-- BACKIFY:MANAGED:END -->";
|
|
17967
17996
|
function managedInstructions() {
|
|
@@ -18413,7 +18442,7 @@ function pushSource(appId, commit, branch, destination, root, environment) {
|
|
|
18413
18442
|
}
|
|
18414
18443
|
async function waitForPreview(call2, prefix, commit, options = {}) {
|
|
18415
18444
|
const deadline = Date.now() + (options.timeoutMs ?? 20 * 6e4);
|
|
18416
|
-
const sleep = options.sleep ?? ((delay) => new Promise((
|
|
18445
|
+
const sleep = options.sleep ?? ((delay) => new Promise((resolve23) => setTimeout(resolve23, delay)));
|
|
18417
18446
|
do {
|
|
18418
18447
|
const deployments = await call2(prefix + "/deployments");
|
|
18419
18448
|
const matching = deployments.find((release2) => release2.kind === "preview" && matchesSource(release2, commit) && !options.previousDeploymentIds?.includes(release2.id));
|
|
@@ -18468,7 +18497,7 @@ async function ship(call2, appId, options = {}) {
|
|
|
18468
18497
|
async function publishPreview(call2, prefix, overview, options = {}) {
|
|
18469
18498
|
if (!overview.preview || overview.preview.status !== "succeeded" || overview.preview.healthResult !== "healthy") throw new Error("Only a verified healthy preview can be published.");
|
|
18470
18499
|
const preview = overview.preview;
|
|
18471
|
-
if (options.previewOnly) return { status: "preview-ready", url: preview.url };
|
|
18500
|
+
if (!options.productionDeploy || options.previewOnly) return { status: "preview-ready", url: preview.url };
|
|
18472
18501
|
if (overview.policy.requirePreviewTesting && !preview.testedAt) return { status: "testing-required", url: preview.url };
|
|
18473
18502
|
if (overview.live?.previewDeploymentId === preview.id && overview.live.status === "succeeded") return { status: "live", url: overview.live.url };
|
|
18474
18503
|
const promotion = await call2(prefix + "/deployments/promote", { method: "POST", headers: { "Idempotency-Key": "cli-ship-" + preview.id }, body: JSON.stringify({ previewDeploymentId: preview.id, tested: false }) });
|
|
@@ -18483,7 +18512,7 @@ async function publishPreview(call2, prefix, overview, options = {}) {
|
|
|
18483
18512
|
if (releases.live?.previewDeploymentId !== preview.id || releases.live.status !== "succeeded" || releases.live.healthResult !== "healthy") throw new Error("Production did not activate this healthy release. Run backify status.");
|
|
18484
18513
|
return { status: "live", url: releases.live.url };
|
|
18485
18514
|
}
|
|
18486
|
-
await new Promise((
|
|
18515
|
+
await new Promise((resolve23) => setTimeout(resolve23, 4e3));
|
|
18487
18516
|
} while (Date.now() < deadline);
|
|
18488
18517
|
throw new Error("Publication is still pending. Run backify status; deployment success has not been verified.");
|
|
18489
18518
|
}
|
|
@@ -18496,8 +18525,8 @@ async function browserAuthenticate(options) {
|
|
|
18496
18525
|
};
|
|
18497
18526
|
let rejectSession = () => {
|
|
18498
18527
|
};
|
|
18499
|
-
const completed = new Promise((
|
|
18500
|
-
resolveSession =
|
|
18528
|
+
const completed = new Promise((resolve23, reject) => {
|
|
18529
|
+
resolveSession = resolve23;
|
|
18501
18530
|
rejectSession = reject;
|
|
18502
18531
|
});
|
|
18503
18532
|
let accepted = false;
|
|
@@ -18750,7 +18779,7 @@ import { existsSync as existsSync11, lstatSync as lstatSync15, mkdirSync as mkdi
|
|
|
18750
18779
|
import { resolve as resolve17, join as join12, relative as relative7, isAbsolute as isAbsolute13, dirname as dirname12 } from "node:path";
|
|
18751
18780
|
|
|
18752
18781
|
// ../analyzer/dist/index.js
|
|
18753
|
-
import
|
|
18782
|
+
import ts3 from "typescript";
|
|
18754
18783
|
import { readFileSync as readFileSync5, readdirSync as readdirSync4, existsSync as existsSync6 } from "node:fs";
|
|
18755
18784
|
import { join as join5, extname } from "node:path";
|
|
18756
18785
|
|
|
@@ -18867,6 +18896,191 @@ function analyzeAuthentication(files) {
|
|
|
18867
18896
|
return { used, providers: [...providers].sort(), dynamicFiles: [...dynamicFiles].sort() };
|
|
18868
18897
|
}
|
|
18869
18898
|
|
|
18899
|
+
// ../analyzer/dist/schedules.js
|
|
18900
|
+
import ts2 from "typescript";
|
|
18901
|
+
var dynamic = Symbol("dynamic");
|
|
18902
|
+
function literal(node) {
|
|
18903
|
+
if (!node)
|
|
18904
|
+
return void 0;
|
|
18905
|
+
if (ts2.isStringLiteral(node) || ts2.isNoSubstitutionTemplateLiteral(node))
|
|
18906
|
+
return node.text;
|
|
18907
|
+
if (ts2.isNumericLiteral(node))
|
|
18908
|
+
return Number(node.text);
|
|
18909
|
+
if (ts2.isPrefixUnaryExpression(node) && node.operator === ts2.SyntaxKind.MinusToken && ts2.isNumericLiteral(node.operand))
|
|
18910
|
+
return -Number(node.operand.text);
|
|
18911
|
+
if (node.kind === ts2.SyntaxKind.TrueKeyword)
|
|
18912
|
+
return true;
|
|
18913
|
+
if (node.kind === ts2.SyntaxKind.FalseKeyword)
|
|
18914
|
+
return false;
|
|
18915
|
+
if (node.kind === ts2.SyntaxKind.NullKeyword)
|
|
18916
|
+
return null;
|
|
18917
|
+
if (ts2.isParenthesizedExpression(node) || ts2.isAsExpression(node) || ts2.isSatisfiesExpression(node))
|
|
18918
|
+
return literal(node.expression);
|
|
18919
|
+
if (ts2.isArrayLiteralExpression(node)) {
|
|
18920
|
+
const values = node.elements.map((item) => literal(item));
|
|
18921
|
+
return values.includes(dynamic) ? dynamic : values;
|
|
18922
|
+
}
|
|
18923
|
+
if (ts2.isObjectLiteralExpression(node)) {
|
|
18924
|
+
const result = {};
|
|
18925
|
+
for (const property of node.properties) {
|
|
18926
|
+
if (!ts2.isPropertyAssignment(property) || !ts2.isIdentifier(property.name) && !ts2.isStringLiteral(property.name))
|
|
18927
|
+
return dynamic;
|
|
18928
|
+
const value = literal(property.initializer);
|
|
18929
|
+
if (value === dynamic)
|
|
18930
|
+
return dynamic;
|
|
18931
|
+
Object.defineProperty(result, property.name.text, { value, enumerable: true });
|
|
18932
|
+
}
|
|
18933
|
+
return result;
|
|
18934
|
+
}
|
|
18935
|
+
return dynamic;
|
|
18936
|
+
}
|
|
18937
|
+
function analyzeSchedules(files) {
|
|
18938
|
+
const schedules = [], diagnostics = [];
|
|
18939
|
+
for (const file of files) {
|
|
18940
|
+
const sourcePath = file.path.replaceAll(String.fromCharCode(92), "/");
|
|
18941
|
+
if (/(^|\/)(tests?|__tests__|fixtures)\/|\.(test|spec)\.[cm]?[jt]sx?$/.test(sourcePath))
|
|
18942
|
+
continue;
|
|
18943
|
+
const source = ts2.createSourceFile(sourcePath, file.content, ts2.ScriptTarget.Latest, true, file.path.endsWith("x") ? ts2.ScriptKind.TSX : ts2.ScriptKind.TS);
|
|
18944
|
+
const program = ts2.createProgram([sourcePath], { noLib: true, noResolve: true, allowJs: true }, {
|
|
18945
|
+
getSourceFile: (path) => path === sourcePath ? source : void 0,
|
|
18946
|
+
getDefaultLibFileName: () => "",
|
|
18947
|
+
writeFile: () => {
|
|
18948
|
+
},
|
|
18949
|
+
getCurrentDirectory: () => "/",
|
|
18950
|
+
getDirectories: () => [],
|
|
18951
|
+
fileExists: (path) => path === sourcePath,
|
|
18952
|
+
readFile: (path) => path === sourcePath ? file.content : void 0,
|
|
18953
|
+
getCanonicalFileName: (path) => path,
|
|
18954
|
+
useCaseSensitiveFileNames: () => true,
|
|
18955
|
+
getNewLine: () => "\n"
|
|
18956
|
+
});
|
|
18957
|
+
const checker = program.getTypeChecker();
|
|
18958
|
+
const jobSymbols = /* @__PURE__ */ new Set(), scheduleSymbols = /* @__PURE__ */ new Set(), roots = /* @__PURE__ */ new Set(), namespaces = /* @__PURE__ */ new Set(), factories = /* @__PURE__ */ new Set();
|
|
18959
|
+
const has = (symbols, node) => {
|
|
18960
|
+
const symbol = checker.getSymbolAtLocation(node);
|
|
18961
|
+
return !!symbol && symbols.has(symbol);
|
|
18962
|
+
};
|
|
18963
|
+
const add = (symbols, node) => {
|
|
18964
|
+
const symbol = checker.getSymbolAtLocation(node);
|
|
18965
|
+
if (symbol)
|
|
18966
|
+
symbols.add(symbol);
|
|
18967
|
+
};
|
|
18968
|
+
for (const statement of source.statements) {
|
|
18969
|
+
if (!ts2.isImportDeclaration(statement) || !ts2.isStringLiteral(statement.moduleSpecifier) || !["@backifydev/sdk", "@backifydev/sdk/server"].includes(statement.moduleSpecifier.text) || statement.importClause?.isTypeOnly)
|
|
18970
|
+
continue;
|
|
18971
|
+
const bindings = statement.importClause?.namedBindings;
|
|
18972
|
+
if (bindings && ts2.isNamespaceImport(bindings))
|
|
18973
|
+
add(namespaces, bindings.name);
|
|
18974
|
+
if (bindings && ts2.isNamedImports(bindings))
|
|
18975
|
+
for (const binding of bindings.elements) {
|
|
18976
|
+
if (binding.isTypeOnly)
|
|
18977
|
+
continue;
|
|
18978
|
+
const name2 = (binding.propertyName ?? binding.name).text;
|
|
18979
|
+
if (name2 === "jobs")
|
|
18980
|
+
add(jobSymbols, binding.name);
|
|
18981
|
+
if (name2 === "backify")
|
|
18982
|
+
add(roots, binding.name);
|
|
18983
|
+
if (name2 === "createServerClient")
|
|
18984
|
+
add(factories, binding.name);
|
|
18985
|
+
}
|
|
18986
|
+
}
|
|
18987
|
+
const isNamespace = (node) => ts2.isIdentifier(node) && has(namespaces, node);
|
|
18988
|
+
const isRoot = (node) => ts2.isIdentifier(node) && has(roots, node) || ts2.isPropertyAccessExpression(node) && node.name.text === "backify" && isNamespace(node.expression) || ts2.isCallExpression(node) && (ts2.isIdentifier(node.expression) && has(factories, node.expression) || ts2.isPropertyAccessExpression(node.expression) && node.expression.name.text === "createServerClient" && isNamespace(node.expression.expression));
|
|
18989
|
+
const isJobs = (node) => ts2.isIdentifier(node) && has(jobSymbols, node) || ts2.isPropertyAccessExpression(node) && node.name.text === "jobs" && (isRoot(node.expression) || isNamespace(node.expression));
|
|
18990
|
+
const collect = (node) => {
|
|
18991
|
+
if (ts2.isVariableDeclaration(node) && node.initializer) {
|
|
18992
|
+
if (ts2.isIdentifier(node.name)) {
|
|
18993
|
+
if (isRoot(node.initializer))
|
|
18994
|
+
add(roots, node.name);
|
|
18995
|
+
if (isJobs(node.initializer))
|
|
18996
|
+
add(jobSymbols, node.name);
|
|
18997
|
+
if (ts2.isPropertyAccessExpression(node.initializer) && node.initializer.name.text === "schedule" && isJobs(node.initializer.expression) || ts2.isIdentifier(node.initializer) && has(scheduleSymbols, node.initializer))
|
|
18998
|
+
add(scheduleSymbols, node.name);
|
|
18999
|
+
} else if (ts2.isObjectBindingPattern(node.name)) {
|
|
19000
|
+
for (const binding of node.name.elements) {
|
|
19001
|
+
if (!ts2.isIdentifier(binding.name) || binding.dotDotDotToken || binding.initializer)
|
|
19002
|
+
continue;
|
|
19003
|
+
const propertyName = binding.propertyName ?? binding.name;
|
|
19004
|
+
if (!ts2.isIdentifier(propertyName) && !ts2.isStringLiteral(propertyName))
|
|
19005
|
+
continue;
|
|
19006
|
+
if (propertyName.text === "jobs" && (isRoot(node.initializer) || isNamespace(node.initializer)))
|
|
19007
|
+
add(jobSymbols, binding.name);
|
|
19008
|
+
if (propertyName.text === "schedule" && isJobs(node.initializer))
|
|
19009
|
+
add(scheduleSymbols, binding.name);
|
|
19010
|
+
if (propertyName.text === "backify" && isNamespace(node.initializer))
|
|
19011
|
+
add(roots, binding.name);
|
|
19012
|
+
if (propertyName.text === "createServerClient" && isNamespace(node.initializer))
|
|
19013
|
+
add(factories, binding.name);
|
|
19014
|
+
}
|
|
19015
|
+
}
|
|
19016
|
+
}
|
|
19017
|
+
ts2.forEachChild(node, collect);
|
|
19018
|
+
};
|
|
19019
|
+
let previousSize = -1;
|
|
19020
|
+
while (previousSize !== jobSymbols.size + scheduleSymbols.size + roots.size + factories.size) {
|
|
19021
|
+
previousSize = jobSymbols.size + scheduleSymbols.size + roots.size + factories.size;
|
|
19022
|
+
collect(source);
|
|
19023
|
+
}
|
|
19024
|
+
const report = (code, message, severity = "warning") => diagnostics.push({ code, message, severity, file: file.path, docs: "https://docs.backify.dev/capabilities/scheduler" });
|
|
19025
|
+
const property = (node, name2) => node.properties.find((item) => ts2.isPropertyAssignment(item) && (ts2.isIdentifier(item.name) || ts2.isStringLiteral(item.name)) && item.name.text === name2);
|
|
19026
|
+
const visit = (node) => {
|
|
19027
|
+
if (ts2.isCallExpression(node) && ts2.isPropertyAccessExpression(node.expression)) {
|
|
19028
|
+
const method = node.expression.name.text, receiver = node.expression.expression;
|
|
19029
|
+
let name2, definition;
|
|
19030
|
+
if (method === "define" && isJobs(receiver) && node.arguments[0] && ts2.isObjectLiteralExpression(node.arguments[0])) {
|
|
19031
|
+
const schedule = property(node.arguments[0], "schedule");
|
|
19032
|
+
if (schedule) {
|
|
19033
|
+
name2 = literal(property(node.arguments[0], "name")?.initializer);
|
|
19034
|
+
definition = node.arguments[0].properties.some((item) => ts2.isSpreadAssignment(item) || item.name && ts2.isComputedPropertyName(item.name)) ? dynamic : literal(schedule.initializer);
|
|
19035
|
+
} else if (node.arguments[0].properties.some((item) => ts2.isShorthandPropertyAssignment(item) && item.name.text === "schedule")) {
|
|
19036
|
+
definition = dynamic;
|
|
19037
|
+
}
|
|
19038
|
+
} else if ((ts2.isPropertyAccessExpression(receiver) && receiver.name.text === "schedule" && isJobs(receiver.expression) || ts2.isIdentifier(receiver) && has(scheduleSymbols, receiver)) && ["daily", "weekly", "hourly", "cron", "once", "interval"].includes(method)) {
|
|
19039
|
+
name2 = literal(node.arguments[0]);
|
|
19040
|
+
definition = { kind: method, ...method === "weekly" ? { day: literal(node.arguments[2]) ?? "monday", at: literal(node.arguments[3]) ?? "00:00", timeZone: literal(node.arguments[4]) ?? "UTC" } : method === "interval" ? { intervalSeconds: literal(node.arguments[2]) } : method === "cron" ? { expression: literal(node.arguments[2]), timeZone: literal(node.arguments[3]) ?? "UTC" } : { at: literal(node.arguments[2]), timeZone: literal(node.arguments[3]) ?? "UTC" } };
|
|
19041
|
+
}
|
|
19042
|
+
if (definition !== void 0) {
|
|
19043
|
+
if (typeof name2 !== "string" || !definition || typeof definition !== "object" || Object.values(definition).includes(dynamic))
|
|
19044
|
+
report("dynamic-schedule", "Use literal schedule declarations for automatic deployment provisioning, or jobs.schedule.sync() in a trusted server setup step for dynamic declarations.");
|
|
19045
|
+
else {
|
|
19046
|
+
const settings = definition, kind = settings.kind;
|
|
19047
|
+
const schedule = { name: name2, job: name2, kind: String(kind), timeZone: String(settings.timeZone ?? "UTC"), ...settings.payload !== void 0 ? { payload: settings.payload } : {} };
|
|
19048
|
+
if (["daily", "weekly", "hourly"].includes(String(kind))) {
|
|
19049
|
+
const time = settings.at ?? "00:00", day = ["sunday", "monday", "tuesday", "wednesday", "thursday", "friday", "saturday"].indexOf(String(settings.day ?? "monday").toLowerCase());
|
|
19050
|
+
if (typeof time !== "string" || !/^([01]\d|2[0-3]):[0-5]\d$/.test(time) || kind === "weekly" && day < 0)
|
|
19051
|
+
report("invalid-schedule", "Use HH:mm and a valid weekday for scheduled jobs.", "error");
|
|
19052
|
+
else {
|
|
19053
|
+
const [hour, minute] = time.split(":").map(Number);
|
|
19054
|
+
schedule.kind = "cron";
|
|
19055
|
+
schedule.expression = kind === "hourly" ? "0 * * * *" : minute + " " + hour + " * * " + (kind === "weekly" ? day : "*");
|
|
19056
|
+
schedules.push(schedule);
|
|
19057
|
+
}
|
|
19058
|
+
} else if (kind === "cron" || kind === "once" || kind === "interval") {
|
|
19059
|
+
if (kind === "cron")
|
|
19060
|
+
schedule.expression = settings.expression;
|
|
19061
|
+
if (kind === "once")
|
|
19062
|
+
schedule.at = settings.at;
|
|
19063
|
+
if (kind === "interval")
|
|
19064
|
+
schedule.intervalSeconds = settings.intervalSeconds;
|
|
19065
|
+
schedules.push(schedule);
|
|
19066
|
+
} else
|
|
19067
|
+
report("invalid-schedule", "Unknown schedule declaration kind.", "error");
|
|
19068
|
+
}
|
|
19069
|
+
}
|
|
19070
|
+
}
|
|
19071
|
+
ts2.forEachChild(node, visit);
|
|
19072
|
+
};
|
|
19073
|
+
visit(source);
|
|
19074
|
+
}
|
|
19075
|
+
const unique = /* @__PURE__ */ new Map();
|
|
19076
|
+
for (const schedule of schedules) {
|
|
19077
|
+
if (unique.has(schedule.name) && JSON.stringify(unique.get(schedule.name)) !== JSON.stringify(schedule))
|
|
19078
|
+
diagnostics.push({ severity: "error", code: "duplicate-schedule", message: "Conflicting schedule declarations: " + schedule.name, docs: "https://docs.backify.dev/capabilities/scheduler" });
|
|
19079
|
+
unique.set(schedule.name, schedule);
|
|
19080
|
+
}
|
|
19081
|
+
return { schedules: [...unique.values()], diagnostics };
|
|
19082
|
+
}
|
|
19083
|
+
|
|
18870
19084
|
// ../analyzer/dist/index.js
|
|
18871
19085
|
var bypass = { "@azure/storage-blob": { capability: "storage", replacement: "storage" }, "@aws-sdk/client-s3": { capability: "storage", replacement: "storage" }, "firebase-admin": { capability: "database", replacement: "db" }, "@supabase/supabase-js": { capability: "database", replacement: "db" }, "@azure/service-bus": { capability: "jobs", replacement: "jobs" } };
|
|
18872
19086
|
function analyzeSources(files, options = {}) {
|
|
@@ -18875,7 +19089,17 @@ function analyzeSources(files, options = {}) {
|
|
|
18875
19089
|
analyzeFile(file, used, diagnostics);
|
|
18876
19090
|
if (used.has("http"))
|
|
18877
19091
|
used.add("events");
|
|
19092
|
+
if (used.has("integrations")) {
|
|
19093
|
+
used.add("events");
|
|
19094
|
+
used.add("secrets");
|
|
19095
|
+
}
|
|
18878
19096
|
const authentication = analyzeAuthentication(files);
|
|
19097
|
+
const scheduling = analyzeSchedules(files);
|
|
19098
|
+
if (scheduling.schedules.length || scheduling.diagnostics.length) {
|
|
19099
|
+
used.add("jobs");
|
|
19100
|
+
used.add("scheduler");
|
|
19101
|
+
}
|
|
19102
|
+
diagnostics.push(...scheduling.diagnostics);
|
|
18879
19103
|
if (authentication.used)
|
|
18880
19104
|
used.add("auth");
|
|
18881
19105
|
else
|
|
@@ -18883,6 +19107,8 @@ function analyzeSources(files, options = {}) {
|
|
|
18883
19107
|
const capabilities = Object.fromEntries([...used].sort().map((name2) => [name2, { version: CAPABILITY_REGISTRY[name2].version }]));
|
|
18884
19108
|
if (capabilities.auth && (authentication.providers.length || authentication.dynamicFiles.length))
|
|
18885
19109
|
capabilities.auth.configuration = { providers: authentication.providers, dynamicProviders: authentication.dynamicFiles.length > 0 };
|
|
19110
|
+
if (capabilities.scheduler && scheduling.schedules.length)
|
|
19111
|
+
capabilities.scheduler.configuration = { schedules: scheduling.schedules };
|
|
18886
19112
|
for (const file of authentication.dynamicFiles)
|
|
18887
19113
|
diagnostics.push({ severity: "warning", code: "dynamic-auth-provider", file, docs: CAPABILITY_REGISTRY.auth.docs, message: "Declare every possible authentication provider in backify.yaml; dynamic provider selection cannot be provisioned from source alone." });
|
|
18888
19114
|
for (const provider of authentication.providers)
|
|
@@ -18891,9 +19117,9 @@ function analyzeSources(files, options = {}) {
|
|
|
18891
19117
|
return { schemaVersion: 1, applicationId: options.applicationId ?? "unknown", commit: options.commit ?? "unknown", sdkVersion: options.sdkVersion ?? "unknown", capabilities, diagnostics };
|
|
18892
19118
|
}
|
|
18893
19119
|
function analyzeFile(file, used, diagnostics) {
|
|
18894
|
-
const source =
|
|
19120
|
+
const source = ts3.createSourceFile(file.path, file.content, ts3.ScriptTarget.Latest, true, file.path.endsWith("x") ? ts3.ScriptKind.TSX : ts3.ScriptKind.TS), named = /* @__PURE__ */ new Map(), namespaces = /* @__PURE__ */ new Set(), roots = /* @__PURE__ */ new Set(), factories = /* @__PURE__ */ new Set();
|
|
18895
19121
|
source.forEachChild((node) => {
|
|
18896
|
-
if (!
|
|
19122
|
+
if (!ts3.isImportDeclaration(node) || !ts3.isStringLiteral(node.moduleSpecifier))
|
|
18897
19123
|
return;
|
|
18898
19124
|
const module = node.moduleSpecifier.text;
|
|
18899
19125
|
if (bypass[module]) {
|
|
@@ -18905,9 +19131,9 @@ function analyzeFile(file, used, diagnostics) {
|
|
|
18905
19131
|
const bindings = node.importClause.namedBindings;
|
|
18906
19132
|
if (!bindings)
|
|
18907
19133
|
return;
|
|
18908
|
-
if (
|
|
19134
|
+
if (ts3.isNamespaceImport(bindings))
|
|
18909
19135
|
namespaces.add(bindings.name.text);
|
|
18910
|
-
else if (
|
|
19136
|
+
else if (ts3.isNamedImports(bindings))
|
|
18911
19137
|
for (const element of bindings.elements) {
|
|
18912
19138
|
const imported = (element.propertyName ?? element.name).text, local = element.name.text;
|
|
18913
19139
|
if (imported === "createServerClient")
|
|
@@ -18922,17 +19148,19 @@ function analyzeFile(file, used, diagnostics) {
|
|
|
18922
19148
|
}
|
|
18923
19149
|
});
|
|
18924
19150
|
const visit = (node) => {
|
|
18925
|
-
if (
|
|
19151
|
+
if (ts3.isCallExpression(node) && ts3.isPropertyAccessExpression(node.expression) && ["createWebhook", "create"].includes(node.expression.name.text) && rootCapability(node.expression.expression, named, namespaces, roots) === "events")
|
|
19152
|
+
used.add("functions");
|
|
19153
|
+
if (ts3.isVariableDeclaration(node) && ts3.isIdentifier(node.name) && node.initializer && ts3.isCallExpression(node.initializer) && ts3.isIdentifier(node.initializer.expression) && factories.has(node.initializer.expression.text))
|
|
18926
19154
|
roots.add(node.name.text);
|
|
18927
|
-
if (
|
|
19155
|
+
if (ts3.isCallExpression(node) && ts3.isIdentifier(node.expression) && named.get(node.expression.text) === "jobs" && node.arguments.some((argument) => ts3.isObjectLiteralExpression(argument) && argument.properties.some((property) => ts3.isPropertyAssignment(property) && property.name.getText(source) === "schedule")))
|
|
18928
19156
|
used.add("scheduler");
|
|
18929
|
-
if (
|
|
19157
|
+
if (ts3.isIdentifier(node)) {
|
|
18930
19158
|
const parent = node.parent;
|
|
18931
|
-
if (named.has(node.text) && !
|
|
19159
|
+
if (named.has(node.text) && !ts3.isImportSpecifier(parent) && isValueUse(node))
|
|
18932
19160
|
used.add(named.get(node.text));
|
|
18933
19161
|
}
|
|
18934
|
-
if (
|
|
18935
|
-
if (
|
|
19162
|
+
if (ts3.isPropertyAccessExpression(node)) {
|
|
19163
|
+
if (ts3.isIdentifier(node.expression)) {
|
|
18936
19164
|
if (namespaces.has(node.expression.text)) {
|
|
18937
19165
|
const cap = SDK_IMPORT_CAPABILITIES[node.name.text];
|
|
18938
19166
|
if (cap)
|
|
@@ -18947,18 +19175,18 @@ function analyzeFile(file, used, diagnostics) {
|
|
|
18947
19175
|
if (node.name.text === "schedule" && rootCapability(node.expression, named, namespaces, roots) === "jobs")
|
|
18948
19176
|
used.add("scheduler");
|
|
18949
19177
|
}
|
|
18950
|
-
|
|
19178
|
+
ts3.forEachChild(node, visit);
|
|
18951
19179
|
};
|
|
18952
19180
|
visit(source);
|
|
18953
19181
|
}
|
|
18954
19182
|
function isValueUse(node) {
|
|
18955
19183
|
const p = node.parent;
|
|
18956
|
-
return !(
|
|
19184
|
+
return !(ts3.isImportSpecifier(p) || ts3.isExportSpecifier(p) || ts3.isTypeReferenceNode(p) || ts3.isTypeQueryNode(p));
|
|
18957
19185
|
}
|
|
18958
19186
|
function rootCapability(node, named, namespaces, roots) {
|
|
18959
|
-
if (
|
|
19187
|
+
if (ts3.isIdentifier(node))
|
|
18960
19188
|
return named.get(node.text);
|
|
18961
|
-
if (
|
|
19189
|
+
if (ts3.isPropertyAccessExpression(node) && ts3.isIdentifier(node.expression)) {
|
|
18962
19190
|
if (namespaces.has(node.expression.text) || roots.has(node.expression.text))
|
|
18963
19191
|
return SDK_IMPORT_CAPABILITIES[node.name.text];
|
|
18964
19192
|
}
|
|
@@ -19138,6 +19366,10 @@ function createLocalNativeEvents(local, native) {
|
|
|
19138
19366
|
if (typeof name2 !== "string" || !/^[a-zA-Z][a-zA-Z0-9_.-]{0,127}$/.test(name2) || /[\r\n]/.test(name2)) throw new Error("Invalid event name");
|
|
19139
19367
|
};
|
|
19140
19368
|
return {
|
|
19369
|
+
track: (name2, data, options) => {
|
|
19370
|
+
if (!local.track) throw new Error("Upgrade the local SDK to enable event tracking");
|
|
19371
|
+
return local.track(name2, data, options);
|
|
19372
|
+
},
|
|
19141
19373
|
async emit(name2, payload, options) {
|
|
19142
19374
|
validateName2(name2);
|
|
19143
19375
|
if (options?.idempotencyKey !== void 0 && (typeof options.idempotencyKey !== "string" || !options.idempotencyKey || options.idempotencyKey.length > 128)) throw new Error("Invalid idempotency key");
|
|
@@ -20395,10 +20627,10 @@ function assertLocalStatePath(root, filename2) {
|
|
|
20395
20627
|
}
|
|
20396
20628
|
}
|
|
20397
20629
|
function stateValid(value) {
|
|
20398
|
-
const
|
|
20630
|
+
const uuid4 = /^[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}$/i;
|
|
20399
20631
|
if (value?.jobs !== void 0 && !validLocalJobState(value.jobs, [value.databaseId, value.storageId, ...Object.values(value.storageBindings ?? {}), ...value.events ? [value.events.id] : []].filter((identifier2) => typeof identifier2 === "string"))) return false;
|
|
20400
|
-
if (value?.events !== void 0 && (!value.events || typeof value.events !== "object" || Array.isArray(value.events) || typeof value.events.id !== "string" || value.events.id.length !== 36 || !
|
|
20401
|
-
return value?.version === 1 && value.scope && [value.scope.organizationId, value.scope.applicationId, value.scope.environmentId, value.databaseId, value.storageId].every((identifier2) => typeof identifier2 === "string" &&
|
|
20632
|
+
if (value?.events !== void 0 && (!value.events || typeof value.events !== "object" || Array.isArray(value.events) || typeof value.events.id !== "string" || value.events.id.length !== 36 || !uuid4.test(value.events.id) || value.events.id === "00000000-0000-0000-0000-000000000000" || [value.databaseId, value.storageId, ...Object.values(value.storageBindings ?? {})].some((identifier2) => typeof identifier2 === "string" && identifier2.toLowerCase() === value.events.id.toLowerCase()) || typeof value.events.password !== "string" || value.events.password.length !== 64 || !/^[a-f0-9]{64}$/.test(value.events.password) || typeof value.events.schemaDigest !== "string" || value.events.schemaDigest.length !== 64 || !/^[a-f0-9]{64}$/.test(value.events.schemaDigest) || typeof value.events.initialized !== "boolean")) return false;
|
|
20633
|
+
return value?.version === 1 && value.scope && [value.scope.organizationId, value.scope.applicationId, value.scope.environmentId, value.databaseId, value.storageId].every((identifier2) => typeof identifier2 === "string" && uuid4.test(identifier2)) && Number.isSafeInteger(value.configurationVersion) && value.configurationVersion >= 0 && /^[a-f0-9]{64}$/.test(value.adminPassword) && /^[a-f0-9]{64}$/.test(value.databasePassword) && (value.retainedDatabasePort === void 0 || Number.isInteger(value.retainedDatabasePort) && value.retainedDatabasePort > 0 && value.retainedDatabasePort <= 65535);
|
|
20402
20634
|
}
|
|
20403
20635
|
function acquireLocalLease(root, relativePath2 = ".backify/dev/development.lock") {
|
|
20404
20636
|
const filename2 = join7(root, relativePath2);
|
|
@@ -20968,8 +21200,8 @@ function prepareLocalJobTrustRenewal(root, filename2, mount) {
|
|
|
20968
21200
|
}
|
|
20969
21201
|
}
|
|
20970
21202
|
function createLocalJobTrustRenewal(root, mounts, settings = {}, dependencies = { createHash: createHash9, lstatSync: lstatSync10, readFileSync: readFileSync11, isAbsolute: isAbsolute7, relative: relative2, resolve: resolve10, createPersistentApplicationAuthProvider, synchronizeApplicationAuthTrustWithDigest }) {
|
|
20971
|
-
const { createHash: createHash16, lstatSync:
|
|
20972
|
-
const key = (filename2) => process.platform === "win32" ?
|
|
21203
|
+
const { createHash: createHash16, lstatSync: lstatSync19, readFileSync: readFileSync24, isAbsolute: isAbsolute17, relative: relative11, resolve: resolve23, createPersistentApplicationAuthProvider: createPersistentApplicationAuthProvider2, synchronizeApplicationAuthTrustWithDigest: synchronizeApplicationAuthTrustWithDigest2 } = dependencies;
|
|
21204
|
+
const key = (filename2) => process.platform === "win32" ? resolve23(filename2).toLowerCase() : resolve23(filename2);
|
|
20973
21205
|
const groups = [];
|
|
20974
21206
|
for (const mount of mounts) {
|
|
20975
21207
|
if (!mount.renewal) continue;
|
|
@@ -20986,13 +21218,13 @@ function createLocalJobTrustRenewal(root, mounts, settings = {}, dependencies =
|
|
|
20986
21218
|
groups.push({ mount, followers, renewal, nextAt: 0, failures: 0, status: "waiting" });
|
|
20987
21219
|
}
|
|
20988
21220
|
const read = (filename2, maximum, privateFile2 = false) => {
|
|
20989
|
-
for (let current =
|
|
21221
|
+
for (let current = resolve23(filename2); current !== root; current = resolve23(current, "..")) {
|
|
20990
21222
|
const part = relative11(root, current);
|
|
20991
|
-
if (part === ".." || part.startsWith("../") || part.startsWith("..\\") || isAbsolute17(part) ||
|
|
21223
|
+
if (part === ".." || part.startsWith("../") || part.startsWith("..\\") || isAbsolute17(part) || lstatSync19(current).isSymbolicLink()) throw new Error();
|
|
20992
21224
|
}
|
|
20993
|
-
const metadata =
|
|
21225
|
+
const metadata = lstatSync19(filename2);
|
|
20994
21226
|
if (!metadata.isFile() || metadata.nlink !== 1 || metadata.size > maximum || privateFile2 && process.platform !== "win32" && (metadata.uid !== process.getuid?.() || (metadata.mode & 63) !== 0)) throw new Error();
|
|
20995
|
-
const bytes =
|
|
21227
|
+
const bytes = readFileSync24(filename2);
|
|
20996
21228
|
if (bytes.length > maximum) throw new Error();
|
|
20997
21229
|
return bytes;
|
|
20998
21230
|
};
|
|
@@ -21094,25 +21326,25 @@ function readLocalJobArtifacts(root, filename2) {
|
|
|
21094
21326
|
return validateLocalJobArtifacts(JSON.parse(readFileSync12(path, "utf8")));
|
|
21095
21327
|
}
|
|
21096
21328
|
function createLocalJobExecutor(options, dependencies = { spawn: spawn2, createHash: createHash10, lstatSync: lstatSync11, readFileSync: readFileSync12, isAbsolute: isAbsolute8, relative: relative3, resolve: resolve11, validateWorkloadJobSchedules, createPersistentApplicationAuthProvider }) {
|
|
21097
|
-
const { spawn: spawn6, createHash: createHash16, lstatSync:
|
|
21329
|
+
const { spawn: spawn6, createHash: createHash16, lstatSync: lstatSync19, readFileSync: readFileSync24, isAbsolute: isAbsolute17, relative: relative11, resolve: resolve23, validateWorkloadJobSchedules: validateWorkloadJobSchedules2, createPersistentApplicationAuthProvider: createPersistentApplicationAuthProvider2 } = dependencies;
|
|
21098
21330
|
const assertReady = () => {
|
|
21099
|
-
for (let current =
|
|
21331
|
+
for (let current = resolve23(options.workerFile); current !== options.root; current = resolve23(current, "..")) {
|
|
21100
21332
|
const part = relative11(options.root, current);
|
|
21101
|
-
if (part === ".." || part.startsWith("../") || part.startsWith("..\\") || isAbsolute17(part) ||
|
|
21333
|
+
if (part === ".." || part.startsWith("../") || part.startsWith("..\\") || isAbsolute17(part) || lstatSync19(current).isSymbolicLink()) throw new Error("Native worker paths or pins changed");
|
|
21102
21334
|
}
|
|
21103
|
-
if (!
|
|
21335
|
+
if (!lstatSync19(options.workerFile).isFile() || lstatSync19(options.workerFile).size > 16777216 || createHash16("sha256").update(readFileSync24(options.workerFile)).digest("hex") !== options.artifact.artifactVersion) throw new Error("Native worker paths or pins changed");
|
|
21104
21336
|
if (options.authTrust) {
|
|
21105
21337
|
try {
|
|
21106
21338
|
const mount = options.authTrust;
|
|
21107
21339
|
for (const path of [mount.trustFile, mount.stateFile]) {
|
|
21108
|
-
for (let current =
|
|
21340
|
+
for (let current = resolve23(path); current !== options.root; current = resolve23(current, "..")) {
|
|
21109
21341
|
const part = relative11(options.root, current);
|
|
21110
|
-
if (part === ".." || part.startsWith("../") || part.startsWith("..\\") || isAbsolute17(part) ||
|
|
21342
|
+
if (part === ".." || part.startsWith("../") || part.startsWith("..\\") || isAbsolute17(part) || lstatSync19(current).isSymbolicLink()) throw new Error();
|
|
21111
21343
|
}
|
|
21112
|
-
const metadata =
|
|
21344
|
+
const metadata = lstatSync19(path);
|
|
21113
21345
|
if (!metadata.isFile() || metadata.nlink !== 1 || metadata.size > (path === mount.trustFile ? 131072 : 2048)) throw new Error();
|
|
21114
21346
|
}
|
|
21115
|
-
const bytes =
|
|
21347
|
+
const bytes = readFileSync24(mount.trustFile);
|
|
21116
21348
|
if (bytes.length > 131072 || createHash16("sha256").update(bytes).digest("hex") !== mount.trustDigest) throw new Error();
|
|
21117
21349
|
const trust = JSON.parse(new TextDecoder("utf8", { fatal: true }).decode(bytes));
|
|
21118
21350
|
if (trust.revision !== mount.revision) throw new Error();
|
|
@@ -21735,14 +21967,14 @@ function createLocalIdentityProvider(options) {
|
|
|
21735
21967
|
serverOnly();
|
|
21736
21968
|
if (typeof process !== "undefined" && process.env.NODE_ENV === "production")
|
|
21737
21969
|
throw new Error("Local Backify identities must not run in production");
|
|
21738
|
-
const
|
|
21970
|
+
const uuid4 = (value) => typeof value === "string" && /^[a-f0-9]{8}-[a-f0-9]{4}-[a-f0-9]{4}-[a-f0-9]{4}-[a-f0-9]{12}$/.test(value) && value !== "00000000-0000-0000-0000-000000000000";
|
|
21739
21971
|
const text3 = (value, maximum) => typeof value === "string" && value.length > 0 && value.length <= maximum && !/[\x00-\x1f\x7f]/.test(value);
|
|
21740
|
-
if (!options.scope || Object.keys(options.scope).length !== 3 || !
|
|
21972
|
+
if (!options.scope || Object.keys(options.scope).length !== 3 || !uuid4(options.scope.organizationId) || !uuid4(options.scope.applicationId) || !uuid4(options.scope.environmentId) || !text3(options.organizationName, 200) || !Array.isArray(options.users) || options.users.length < 1 || options.users.length > 32 || !Array.isArray(options.requestOrigins) || options.requestOrigins.length < 1 || options.requestOrigins.length > 8)
|
|
21741
21973
|
throw new Error("Invalid local identity configuration");
|
|
21742
21974
|
const scope = { ...options.scope }, organizationName = options.organizationName;
|
|
21743
21975
|
const users = /* @__PURE__ */ new Map();
|
|
21744
21976
|
for (const user of options.users) {
|
|
21745
|
-
if (!user || Object.keys(user).some((name2) => !["id", "displayName", "email", "roles"].includes(name2)) || !
|
|
21977
|
+
if (!user || Object.keys(user).some((name2) => !["id", "displayName", "email", "roles"].includes(name2)) || !uuid4(user.id) || users.has(user.id) || !text3(user.displayName, 200) || user.email !== void 0 && !text3(user.email, 320))
|
|
21746
21978
|
throw new Error("Invalid local identity user");
|
|
21747
21979
|
if (user.roles !== void 0 && (!Array.isArray(user.roles) || user.roles.length > 32 || user.roles.some((role) => !role || Object.keys(role).some((name2) => !["name", "permissions"].includes(name2)) || !text3(role.name, 100) || !Array.isArray(role.permissions) || role.permissions.length > 128 || role.permissions.some((permission) => !text3(permission, 100)) || new Set(role.permissions).size !== role.permissions.length) || new Set(user.roles.map((role) => role.name)).size !== user.roles.length))
|
|
21748
21980
|
throw new Error("Invalid local identity roles");
|
|
@@ -22890,76 +23122,9 @@ async function workloadProvenanceCommand(args2) {
|
|
|
22890
23122
|
return { schemaVersion: 1, status: "captured-unsigned", deploymentReady: false, reportDigest: provenanceHash(JSON.stringify(result, null, 2) + "\n") };
|
|
22891
23123
|
}
|
|
22892
23124
|
|
|
22893
|
-
// src/workload-artifact-plan.ts
|
|
22894
|
-
import { existsSync as existsSync13, lstatSync as lstatSync17, readFileSync as readFileSync21, realpathSync as realpathSync7 } from "node:fs";
|
|
22895
|
-
import { resolve as resolve21 } from "node:path";
|
|
22896
|
-
function uuid4(value) {
|
|
22897
|
-
return typeof value === "string" && value.length === 36 && /^[a-f0-9]{8}-[a-f0-9]{4}-[a-f0-9]{4}-[a-f0-9]{4}-[a-f0-9]{12}$/i.test(value) && value !== "00000000-0000-0000-0000-000000000000";
|
|
22898
|
-
}
|
|
22899
|
-
function validImage(value) {
|
|
22900
|
-
if (typeof value !== "string" || value.length > 512 || /[\r\n]/.test(value) || !/^[a-z0-9][a-z0-9.:-]*\/[a-z0-9]+(?:(?:[._]|__|-+)[a-z0-9]+)*(?:\/[a-z0-9]+(?:(?:[._]|__|-+)[a-z0-9]+)*)*@sha256:[a-f0-9]{64}$/.test(value)) return false;
|
|
22901
|
-
const registry = value.slice(0, value.indexOf("/")), parts = registry.split(":");
|
|
22902
|
-
if (parts.length > 2 || parts[0].length > 253 || parts[0].split(".").some((label) => !/^[a-z0-9](?:[a-z0-9-]{0,61}[a-z0-9])?$/.test(label)) || parts.length === 2 && (!/^[0-9]+$/.test(parts[1]) || Number(parts[1]) < 1 || Number(parts[1]) > 65535)) return false;
|
|
22903
|
-
return true;
|
|
22904
|
-
}
|
|
22905
|
-
function jobMetadata(value) {
|
|
22906
|
-
if (!Array.isArray(value) || value.length < 1 || value.length > 100) throw new Error("Invalid bounded worker job metadata");
|
|
22907
|
-
const names = /* @__PURE__ */ new Set();
|
|
22908
|
-
return value.map((item) => {
|
|
22909
|
-
if (!item || typeof item !== "object" || Array.isArray(item) || Object.keys(item).some((key) => !["name", "retries", "timeoutSeconds", "concurrency", "backoff"].includes(key)) || typeof item.name !== "string" || item.name.length > 100 || /[\r\n]/.test(item.name) || !/^[a-zA-Z0-9][a-zA-Z0-9_-]{0,99}$/.test(item.name) || names.has(item.name)) throw new Error("Invalid bounded worker job metadata");
|
|
22910
|
-
const job = { name: item.name, retries: item.retries === void 0 ? 0 : item.retries, timeoutSeconds: item.timeoutSeconds === void 0 ? 30 : item.timeoutSeconds, concurrency: item.concurrency === void 0 ? 1 : item.concurrency, backoff: item.backoff === void 0 ? "fixed" : item.backoff };
|
|
22911
|
-
if (!Number.isInteger(job.retries) || job.retries < 0 || job.retries > 100 || !Number.isInteger(job.timeoutSeconds) || job.timeoutSeconds < 1 || job.timeoutSeconds > 86400 || !Number.isInteger(job.concurrency) || job.concurrency < 1 || job.concurrency > 100 || !["fixed", "exponential"].includes(job.backoff)) throw new Error("Invalid bounded worker job metadata");
|
|
22912
|
-
names.add(job.name);
|
|
22913
|
-
return job;
|
|
22914
|
-
}).sort((first, second) => first.name < second.name ? -1 : first.name > second.name ? 1 : 0);
|
|
22915
|
-
}
|
|
22916
|
-
function parseWorkerArtifactPlan(args2) {
|
|
22917
|
-
const fields5 = /* @__PURE__ */ new Map([["--root", "root"], ["--organization", "organizationId"], ["--app", "applicationId"], ["--environment", "environmentId"], ["--deployment", "deploymentId"], ["--source-commit", "sourceCommit"], ["--image", "imageReference"], ["--jobs-file", "jobsFile"], ["--entry", "entry"], ["--base-image", "baseImage"]]);
|
|
22918
|
-
const result = {}, seen = /* @__PURE__ */ new Set();
|
|
22919
|
-
for (let index = 0; index < args2.length; index++) {
|
|
22920
|
-
const option2 = args2[index];
|
|
22921
|
-
if (seen.has(option2)) throw new Error("Duplicate worker artifact planning option");
|
|
22922
|
-
seen.add(option2);
|
|
22923
|
-
if (option2 === "--json") continue;
|
|
22924
|
-
const field = fields5.get(option2), value = args2[++index];
|
|
22925
|
-
if (!field || !value || value.startsWith("--")) throw new Error("Unsupported or missing worker artifact planning option");
|
|
22926
|
-
result[field] = value;
|
|
22927
|
-
}
|
|
22928
|
-
for (const field of ["organizationId", "applicationId", "environmentId", "deploymentId", "sourceCommit", "imageReference", "jobsFile", "entry"]) if (!result[field]) throw new Error("Supply all worker artifact scope, source, image, jobs and entry pins");
|
|
22929
|
-
return result;
|
|
22930
|
-
}
|
|
22931
|
-
async function planWorkerArtifactRegistration(options) {
|
|
22932
|
-
if (![options.organizationId, options.applicationId, options.environmentId, options.deploymentId].every(uuid4) || typeof options.sourceCommit !== "string" || ![40, 64].includes(options.sourceCommit.length) || !/^[a-f0-9]+$/.test(options.sourceCommit) || !validImage(options.imageReference)) throw new Error("Invalid worker artifact scope, source or image pin");
|
|
22933
|
-
const root = realpathSync7(resolve21(options.root ?? process.cwd()));
|
|
22934
|
-
if (typeof options.jobsFile !== "string" || !options.jobsFile || typeof options.entry !== "string" || !options.entry) throw new Error("Worker artifact planning requires explicit jobs and entry files");
|
|
22935
|
-
const filename2 = resolve21(root, options.jobsFile);
|
|
22936
|
-
assertLocalStatePath(root, filename2);
|
|
22937
|
-
if (!existsSync13(filename2) || !lstatSync17(filename2).isFile() || lstatSync17(filename2).size > 32768) throw new Error("Worker job metadata file must be a bounded repository file");
|
|
22938
|
-
let jobs;
|
|
22939
|
-
try {
|
|
22940
|
-
jobs = jobMetadata(JSON.parse(readFileSync21(filename2, "utf8")));
|
|
22941
|
-
} catch {
|
|
22942
|
-
throw new Error("Invalid bounded worker job metadata");
|
|
22943
|
-
}
|
|
22944
|
-
const workload = await planWorkload({ root, kind: "worker", entry: options.entry, baseImage: options.baseImage });
|
|
22945
|
-
const scope = { organizationId: options.organizationId.toLowerCase(), applicationId: options.applicationId.toLowerCase(), environmentId: options.environmentId.toLowerCase() };
|
|
22946
|
-
const body = { schemaVersion: 1, deploymentId: options.deploymentId.toLowerCase(), artifactVersion: workload.artifactVersion, imageReference: options.imageReference, sourceCommit: options.sourceCommit, jobs };
|
|
22947
|
-
return {
|
|
22948
|
-
schemaVersion: 1,
|
|
22949
|
-
status: "planned",
|
|
22950
|
-
scope,
|
|
22951
|
-
deploymentReady: false,
|
|
22952
|
-
sourceCommitVerified: false,
|
|
22953
|
-
imageVerified: false,
|
|
22954
|
-
registration: { method: "POST", path: "/api/apps/" + scope.applicationId + "/worker-artifacts/environments/" + scope.environmentId, headers: { "X-Organization-ID": scope.organizationId, "content-type": "application/json" }, body },
|
|
22955
|
-
requiredBindings: workload.capabilityBindings,
|
|
22956
|
-
blockers: [.../* @__PURE__ */ new Set([...workload.blockers, "deployment-source-match-required", "artifact-source-conformance-required", "immutable-image-conformance-required", "job-declaration-conformance-required", "durable-provider-dispatch-required", "workload-credential-broker-required"])].sort()
|
|
22957
|
-
};
|
|
22958
|
-
}
|
|
22959
|
-
|
|
22960
23125
|
// src/workload-trust-state.ts
|
|
22961
|
-
import { lstatSync as
|
|
22962
|
-
import { isAbsolute as isAbsolute16, resolve as
|
|
23126
|
+
import { lstatSync as lstatSync17, readFileSync as readFileSync21 } from "node:fs";
|
|
23127
|
+
import { isAbsolute as isAbsolute16, resolve as resolve21 } from "node:path";
|
|
22963
23128
|
function parseWorkloadTrustState(args2) {
|
|
22964
23129
|
const action = args2[0];
|
|
22965
23130
|
if (action !== "plan" && action !== "initialize") throw new Error("Use backify workload trust-state plan|initialize");
|
|
@@ -22981,12 +23146,12 @@ function parseWorkloadTrustState(args2) {
|
|
|
22981
23146
|
return { action, trustFile: values.get("--trust-file"), stateFile: values.get("--state-file"), expectedRevision: Number(values.get("--revision")), expectedScope: { organizationId: values.get("--organization"), applicationId: values.get("--application"), environmentId: values.get("--environment") }, confirmed: values.has("--confirm") };
|
|
22982
23147
|
}
|
|
22983
23148
|
function workloadTrustState(options) {
|
|
22984
|
-
if (!["plan", "initialize"].includes(options.action) || !isAbsolute16(options.trustFile) || !isAbsolute16(options.stateFile) ||
|
|
23149
|
+
if (!["plan", "initialize"].includes(options.action) || !isAbsolute16(options.trustFile) || !isAbsolute16(options.stateFile) || resolve21(options.trustFile) === resolve21(options.stateFile) || !Number.isSafeInteger(options.expectedRevision) || options.expectedRevision < 1) throw new Error("Specify separate absolute trust/state paths and a positive reviewed revision");
|
|
22985
23150
|
let trust;
|
|
22986
23151
|
try {
|
|
22987
|
-
const metadata =
|
|
23152
|
+
const metadata = lstatSync17(options.trustFile);
|
|
22988
23153
|
if (!metadata.isFile() || metadata.isSymbolicLink() || metadata.nlink !== 1 || metadata.size > 131072) throw new Error();
|
|
22989
|
-
const bytes =
|
|
23154
|
+
const bytes = readFileSync21(options.trustFile);
|
|
22990
23155
|
if (bytes.length > 131072) throw new Error();
|
|
22991
23156
|
trust = JSON.parse(new TextDecoder("utf8", { fatal: true }).decode(bytes));
|
|
22992
23157
|
} catch {
|
|
@@ -22996,14 +23161,14 @@ function workloadTrustState(options) {
|
|
|
22996
23161
|
if (trust.revision !== options.expectedRevision) throw new Error("Application identity trust revision changed; review again");
|
|
22997
23162
|
if (options.action === "initialize") {
|
|
22998
23163
|
if (!options.confirmed) throw new Error("Initialization requires explicit --confirm after reviewing the plan");
|
|
22999
|
-
initializeApplicationAuthState(trust, { expectedScope: options.expectedScope, stateFile:
|
|
23164
|
+
initializeApplicationAuthState(trust, { expectedScope: options.expectedScope, stateFile: resolve21(options.stateFile) });
|
|
23000
23165
|
}
|
|
23001
|
-
return { schemaVersion: 1, status: options.action === "plan" ? "planned" : "initialized", scope: options.expectedScope, revision: trust.revision, validUntil: trust.validUntil, stateFile:
|
|
23166
|
+
return { schemaVersion: 1, status: options.action === "plan" ? "planned" : "initialized", scope: options.expectedScope, revision: trust.revision, validUntil: trust.validUntil, stateFile: resolve21(options.stateFile), requiresPrivatePersistentDirectory: true, recoveryAutomatic: false, productionChanges: false };
|
|
23002
23167
|
}
|
|
23003
23168
|
|
|
23004
23169
|
// src/scaffold.ts
|
|
23005
|
-
import { existsSync as
|
|
23006
|
-
import { dirname as dirname16, join as join16, relative as relative10, resolve as
|
|
23170
|
+
import { existsSync as existsSync13, lstatSync as lstatSync18, mkdirSync as mkdirSync9, readFileSync as readFileSync22, readdirSync as readdirSync8, realpathSync as realpathSync7, writeFileSync as writeFileSync13 } from "node:fs";
|
|
23171
|
+
import { dirname as dirname16, join as join16, relative as relative10, resolve as resolve22 } from "node:path";
|
|
23007
23172
|
function json2(value) {
|
|
23008
23173
|
return JSON.stringify(value, null, 2) + "\n";
|
|
23009
23174
|
}
|
|
@@ -23181,21 +23346,21 @@ provider-readiness claim, cloud deployment or browser authentication is implied.
|
|
|
23181
23346
|
}
|
|
23182
23347
|
function scaffoldFullStack(options) {
|
|
23183
23348
|
const files = fullStackTemplate(options.appId, options.name);
|
|
23184
|
-
const root =
|
|
23185
|
-
if (!
|
|
23349
|
+
const root = resolve22(options.root ?? process.cwd());
|
|
23350
|
+
if (!existsSync13(root) || !lstatSync18(root).isDirectory() || lstatSync18(root).isSymbolicLink()) throw new Error("Scaffold destination must be an existing non-linked directory");
|
|
23186
23351
|
const allowed = /* @__PURE__ */ new Set([...Object.keys(files).map((filename2) => filename2.split("/")[0]), ".git", "node_modules", "package-lock.json", ".backify"]);
|
|
23187
23352
|
if (readdirSync8(root).some((filename2) => !allowed.has(filename2))) throw new Error("Scaffolding requires an empty folder or an unchanged generated starter");
|
|
23188
|
-
const canonicalRoot =
|
|
23353
|
+
const canonicalRoot = realpathSync7(root);
|
|
23189
23354
|
const pending = [];
|
|
23190
23355
|
for (const [filename2, content] of Object.entries(files)) {
|
|
23191
23356
|
const destination = join16(root, filename2);
|
|
23192
23357
|
let current = destination;
|
|
23193
23358
|
while (current !== root) {
|
|
23194
23359
|
try {
|
|
23195
|
-
const stat =
|
|
23360
|
+
const stat = lstatSync18(current);
|
|
23196
23361
|
if (stat.isSymbolicLink() || current !== destination && !stat.isDirectory()) throw new Error("Scaffolding refuses linked or invalid output paths: " + filename2);
|
|
23197
|
-
if (relative10(canonicalRoot,
|
|
23198
|
-
if (current === destination && (!stat.isFile() ||
|
|
23362
|
+
if (relative10(canonicalRoot, realpathSync7(current)).startsWith("..")) throw new Error("Scaffold output must remain inside its directory");
|
|
23363
|
+
if (current === destination && (!stat.isFile() || readFileSync22(current, "utf8") !== content)) throw new Error("Scaffolding refuses to overwrite existing content: " + filename2);
|
|
23199
23364
|
} catch (error) {
|
|
23200
23365
|
if (!(error instanceof Error) || !("code" in error) || error.code !== "ENOENT") throw error;
|
|
23201
23366
|
if (current === destination) pending.push(filename2);
|
|
@@ -23250,16 +23415,16 @@ function localInspectionCommand(args2) {
|
|
|
23250
23415
|
}
|
|
23251
23416
|
|
|
23252
23417
|
// src/index.ts
|
|
23253
|
-
import { readFileSync as
|
|
23418
|
+
import { readFileSync as readFileSync23, existsSync as existsSync14, rmSync as rmSync3 } from "node:fs";
|
|
23254
23419
|
import { homedir as homedir2 } from "node:os";
|
|
23255
23420
|
import { join as join17 } from "node:path";
|
|
23256
23421
|
import { spawn as spawn5 } from "node:child_process";
|
|
23257
23422
|
var configPath = join17(homedir2(), ".backify.json");
|
|
23258
|
-
var userConfig =
|
|
23423
|
+
var userConfig = existsSync14(configPath) ? JSON.parse(readFileSync23(configPath, "utf8")) : {};
|
|
23259
23424
|
var [command2, ...args] = process.argv.slice(2);
|
|
23260
23425
|
async function call(path, init) {
|
|
23261
23426
|
let appId = option("--app");
|
|
23262
|
-
if (!appId &&
|
|
23427
|
+
if (!appId && existsSync14(join17(process.cwd(), "backify.json"))) appId = projectApplication();
|
|
23263
23428
|
const session = appId ? userConfig.sessions?.[sessionKey(appId)] : void 0;
|
|
23264
23429
|
const credential = session ?? { token: userConfig.token ?? "", organizationId: userConfig.organizationId ?? "", apiUrl: process.env.BACKIFY_API_URL ?? userConfig.apiUrl ?? "https://api.backify.dev/api" };
|
|
23265
23430
|
return (appId ? applicationClient(credential, appId) : apiClient(credential))(path, init);
|
|
@@ -23299,6 +23464,13 @@ function applicationClient(session, appId) {
|
|
|
23299
23464
|
}
|
|
23300
23465
|
async function main() {
|
|
23301
23466
|
switch (command2) {
|
|
23467
|
+
case "integrations": {
|
|
23468
|
+
const path = integrationDiscoveryPath(args[0], { applicationId: applicationGuid(option("--app") ?? projectApplication()), connectionId: option("--connection"), operationId: option("--operation"), query: option("--query"), offset: Number(option("--offset") ?? 0) });
|
|
23469
|
+
const value = await call(path);
|
|
23470
|
+
if (args[0] === "types") console.log(value.source);
|
|
23471
|
+
else console.log(JSON.stringify(value, null, 2));
|
|
23472
|
+
break;
|
|
23473
|
+
}
|
|
23302
23474
|
case "workload": {
|
|
23303
23475
|
if (args[0] === "provenance") {
|
|
23304
23476
|
console.log(JSON.stringify(await workloadProvenanceCommand(args.slice(1)), null, 2));
|
|
@@ -23323,11 +23495,7 @@ async function main() {
|
|
|
23323
23495
|
console.log(JSON.stringify(workloadTrustState(parseWorkloadTrustState(args.slice(1))), null, 2));
|
|
23324
23496
|
break;
|
|
23325
23497
|
}
|
|
23326
|
-
if (args[0]
|
|
23327
|
-
console.log(JSON.stringify(await planWorkerArtifactRegistration(parseWorkerArtifactPlan(args.slice(1))), null, 2));
|
|
23328
|
-
break;
|
|
23329
|
-
}
|
|
23330
|
-
if (args[0] !== "plan" && args[0] !== "package") throw new Error("Use backify workload plan|package --kind http|worker, workload artifact-plan or workload provenance capture|verify");
|
|
23498
|
+
if (args[0] !== "plan" && args[0] !== "package") throw new Error("Use backify workload plan|package --kind http|worker or workload provenance capture|verify");
|
|
23331
23499
|
const options = { kind: option("--kind"), entry: option("--entry"), baseImage: option("--base-image"), output: option("--output") };
|
|
23332
23500
|
console.log(JSON.stringify(await (args[0] === "plan" ? planWorkload(options) : packageWorkload(options)), null, 2));
|
|
23333
23501
|
break;
|
|
@@ -23395,7 +23563,7 @@ async function main() {
|
|
|
23395
23563
|
const name2 = option("--name");
|
|
23396
23564
|
if (!name2) throw new Error("Specify --name <job>");
|
|
23397
23565
|
const payload = option("--payload");
|
|
23398
|
-
console.log(JSON.stringify(await localRequest("/v1/jobs/run", { name: name2, payload: payload ? JSON.parse(
|
|
23566
|
+
console.log(JSON.stringify(await localRequest("/v1/jobs/run", { name: name2, payload: payload ? JSON.parse(readFileSync23(payload, "utf8")) : null }), null, 2));
|
|
23399
23567
|
} else if (["retry", "cancel"].includes(args[0])) {
|
|
23400
23568
|
const id = option("--id");
|
|
23401
23569
|
if (!id) throw new Error("Specify --id <run>");
|
|
@@ -23418,7 +23586,7 @@ async function main() {
|
|
|
23418
23586
|
const environmentId = option("--environment");
|
|
23419
23587
|
const packagePath = option("--package");
|
|
23420
23588
|
if (!environmentId || !packagePath || !args.includes("--confirm")) throw new Error("Use backify functions deploy --environment <environment-id> --package <functions.zip> --confirm. This replaces the complete function project.");
|
|
23421
|
-
await deployFunctions(call, appId, applicationGuid(environmentId), new Uint8Array(
|
|
23589
|
+
await deployFunctions(call, appId, applicationGuid(environmentId), new Uint8Array(readFileSync23(packagePath)), { progress: (message) => console.log(message) });
|
|
23422
23590
|
console.log("Functions deployed.");
|
|
23423
23591
|
} else throw new Error("Use backify functions package or backify functions deploy");
|
|
23424
23592
|
break;
|
|
@@ -23447,7 +23615,7 @@ async function main() {
|
|
|
23447
23615
|
userConfig.token = args[0];
|
|
23448
23616
|
userConfig.organizationId = args[1] ?? userConfig.organizationId;
|
|
23449
23617
|
saveUserConfig(configPath, userConfig);
|
|
23450
|
-
} else await browserLogin(option("--app") ?? (
|
|
23618
|
+
} else await browserLogin(option("--app") ?? (existsSync14(join17(process.cwd(), "backify.json")) ? projectApplication() : void 0), option("--organization"));
|
|
23451
23619
|
console.log("Logged in to Backify");
|
|
23452
23620
|
break;
|
|
23453
23621
|
case "logout":
|
|
@@ -23486,7 +23654,7 @@ async function main() {
|
|
|
23486
23654
|
userConfig.sessions[key] ??= session;
|
|
23487
23655
|
saveUserConfig(configPath, userConfig);
|
|
23488
23656
|
if (args.includes("--clone")) await cloneProject(client, appId);
|
|
23489
|
-
else if (
|
|
23657
|
+
else if (existsSync14(join17(process.cwd(), ".git"))) {
|
|
23490
23658
|
const update = await pullProject(client, appId);
|
|
23491
23659
|
console.log(JSON.stringify(update, null, 2));
|
|
23492
23660
|
if (update.status === "blocked") throw new Error(update.reason);
|
|
@@ -23549,21 +23717,22 @@ async function main() {
|
|
|
23549
23717
|
} else if (args[0] === "install") {
|
|
23550
23718
|
const version = option("--version"), sha256 = option("--sha256");
|
|
23551
23719
|
if (!version || !sha256) throw new Error("Use backify runtime install --version vX.Y.Z --sha256 <reviewed-digest> [--archive <local-zip>]");
|
|
23552
|
-
const bytes = option("--archive") ?
|
|
23720
|
+
const bytes = option("--archive") ? readFileSync23(option("--archive")) : await downloadLocalRuntime(version);
|
|
23553
23721
|
console.log(JSON.stringify(installLocalRuntime(bytes, { version, sha256 }), null, 2));
|
|
23554
23722
|
} else throw new Error("Use backify runtime install or backify runtime doctor");
|
|
23555
23723
|
break;
|
|
23556
23724
|
}
|
|
23557
23725
|
case "ship": {
|
|
23726
|
+
if (args.includes("--production-deploy") && args.includes("--preview-only")) throw new Error("--production-deploy cannot be combined with --preview-only.");
|
|
23558
23727
|
const timeout = Number(option("--timeout") ?? "1200");
|
|
23559
23728
|
if (!Number.isFinite(timeout) || timeout < 1 || timeout > 7200) throw new Error("--timeout must be between 1 and 7200 seconds.");
|
|
23560
|
-
console.log(JSON.stringify(await ship(call, projectApplication(), { previewOnly: args.includes("--preview-only"), timeoutMs: timeout * 1e3 }), null, 2));
|
|
23729
|
+
console.log(JSON.stringify(await ship(call, projectApplication(), { productionDeploy: args.includes("--production-deploy"), previewOnly: args.includes("--preview-only"), timeoutMs: timeout * 1e3 }), null, 2));
|
|
23561
23730
|
break;
|
|
23562
23731
|
}
|
|
23563
23732
|
case "request": {
|
|
23564
23733
|
const workspaceId = option("--workspace"), name2 = option("--name"), slug = option("--slug"), description = option("--description"), configurationFile = option("--configuration");
|
|
23565
23734
|
if (!workspaceId || !name2 || !slug || !description) throw new Error("Use backify request --workspace <id> --name <name> --slug <slug> --description <purpose> [--configuration <json-file>]");
|
|
23566
|
-
const configuration = configurationFile ? JSON.parse(
|
|
23735
|
+
const configuration = configurationFile ? JSON.parse(readFileSync23(configurationFile, "utf8")) : void 0;
|
|
23567
23736
|
console.log(await call("/apps/requests", { method: "POST", body: JSON.stringify({ workspaceId, name: name2, slug, description, configuration }) }));
|
|
23568
23737
|
break;
|
|
23569
23738
|
}
|
|
@@ -23581,7 +23750,7 @@ async function main() {
|
|
|
23581
23750
|
else if (args[0] === "test") {
|
|
23582
23751
|
const name2 = option("--name"), payload = option("--payload");
|
|
23583
23752
|
if (!name2 || !payload) throw new Error("Specify --name and --payload <json-file>. Never include secrets in event payloads.");
|
|
23584
|
-
console.log(JSON.stringify(await call(path + "/test", { method: "POST", body: JSON.stringify({ name: name2, payload: JSON.parse(
|
|
23753
|
+
console.log(JSON.stringify(await call(path + "/test", { method: "POST", body: JSON.stringify({ name: name2, payload: JSON.parse(readFileSync23(payload, "utf8")), idempotencyKey: option("--idempotency-key") }) }), null, 2));
|
|
23585
23754
|
} else throw new Error("Use backify events environments|inspect|test");
|
|
23586
23755
|
break;
|
|
23587
23756
|
}
|
|
@@ -23592,7 +23761,7 @@ async function main() {
|
|
|
23592
23761
|
console.log(await call("/apps/" + applicationGuid(args[0]) + "/deployments/preview", { method: "POST", body: JSON.stringify({ sourceRevision: args[1] ?? "latest" }) }));
|
|
23593
23762
|
break;
|
|
23594
23763
|
default:
|
|
23595
|
-
console.log("backify connect | init | doctor | sync | login | logout | disconnect | status | dev | secrets local init|set|list --key-file <external-file> | test --json | inspect tables|rows|buckets|objects|preview|events --local [--json] | identity users|issue|clear --local [--json] | jobs | pull | report [--check] | prompt record --stdin --id <UUID> | ship [--
|
|
23764
|
+
console.log("backify connect | init | doctor | sync | login | logout | disconnect | status | dev | secrets local init|set|list --key-file <external-file> | test --json | inspect tables|rows|buckets|objects|preview|events --local [--json] | identity users|issue|clear --local [--json] | jobs | pull | report [--check] | prompt record --stdin --id <UUID> | ship [--production-deploy] | request | events environments|inspect|test | integrations list|catalog|search|describe|contract|types | workload plan|package|artifact-plan|trust-state|trust-sync|provenance capture|verify | apps | deploy");
|
|
23596
23765
|
}
|
|
23597
23766
|
}
|
|
23598
23767
|
main().catch((error) => {
|