@withandeo/cli 0.19.8 → 0.19.9
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 +26 -0
- package/dist/api.d.ts +27 -0
- package/dist/api.d.ts.map +1 -1
- package/dist/api.js +20 -0
- package/dist/api.js.map +1 -1
- package/dist/artifact.d.ts.map +1 -1
- package/dist/artifact.js +26 -0
- package/dist/artifact.js.map +1 -1
- package/dist/capability-preflight.d.ts.map +1 -1
- package/dist/capability-preflight.js +19 -0
- package/dist/capability-preflight.js.map +1 -1
- package/dist/contracts.d.ts +46 -3
- package/dist/contracts.d.ts.map +1 -1
- package/dist/documentation.d.ts +1 -1
- package/dist/documentation.d.ts.map +1 -1
- package/dist/documentation.js +1 -1
- package/dist/documentation.js.map +1 -1
- package/dist/flagship-contract.d.ts +63 -0
- package/dist/flagship-contract.d.ts.map +1 -0
- package/dist/flagship-contract.js +117 -0
- package/dist/flagship-contract.js.map +1 -0
- package/dist/flagship-package.d.ts +3 -0
- package/dist/flagship-package.d.ts.map +1 -0
- package/dist/flagship-package.js +31 -0
- package/dist/flagship-package.js.map +1 -0
- package/dist/main.d.ts.map +1 -1
- package/dist/main.js +173 -13
- package/dist/main.js.map +1 -1
- package/dist/workflow-dev.d.ts.map +1 -1
- package/dist/workflow-dev.js +2 -0
- package/dist/workflow-dev.js.map +1 -1
- package/dist/workflow-package.d.ts.map +1 -1
- package/dist/workflow-package.js +3 -0
- package/dist/workflow-package.js.map +1 -1
- package/package.json +1 -1
- package/skill/tender-accounts/SKILL.md +1 -0
- package/skill/tender-accounts/references/feature-flags.md +90 -0
- package/skill/tender-accounts/references/managed-source.md +31 -0
|
@@ -0,0 +1,63 @@
|
|
|
1
|
+
/** Shared admission contract. No runtime rules engine: Cloudflare owns evaluation. */
|
|
2
|
+
export type FlagEnvironment = "development" | "preview" | "production";
|
|
3
|
+
export interface FlagBinding {
|
|
4
|
+
binding: string;
|
|
5
|
+
app_id: string;
|
|
6
|
+
}
|
|
7
|
+
export interface FlagBindings {
|
|
8
|
+
flagship: FlagBinding[];
|
|
9
|
+
env: {
|
|
10
|
+
preview: {
|
|
11
|
+
flagship: FlagBinding[];
|
|
12
|
+
};
|
|
13
|
+
production: {
|
|
14
|
+
flagship: FlagBinding[];
|
|
15
|
+
};
|
|
16
|
+
};
|
|
17
|
+
}
|
|
18
|
+
export declare function flagEnvironment(value: unknown): FlagEnvironment;
|
|
19
|
+
export declare function flagBindings(value: unknown): FlagBinding[];
|
|
20
|
+
export declare function parseFlagBindings(value: unknown): FlagBindings | undefined;
|
|
21
|
+
export declare function selectedFlagBindings(value: FlagBindings | undefined, environment: FlagEnvironment): FlagBinding[];
|
|
22
|
+
export declare function flagKey(value: unknown): string;
|
|
23
|
+
export declare function customerIds(value: unknown): string[];
|
|
24
|
+
export declare function rolloutPercentage(value: unknown): number;
|
|
25
|
+
export interface BooleanFlag {
|
|
26
|
+
key: string;
|
|
27
|
+
description?: string;
|
|
28
|
+
enabled: boolean;
|
|
29
|
+
default_variation: "off";
|
|
30
|
+
variations: {
|
|
31
|
+
off: false;
|
|
32
|
+
on: true;
|
|
33
|
+
};
|
|
34
|
+
rules: Array<{
|
|
35
|
+
priority: number;
|
|
36
|
+
conditions: Array<{
|
|
37
|
+
attribute: "customerId";
|
|
38
|
+
operator: "in";
|
|
39
|
+
value: string[];
|
|
40
|
+
}>;
|
|
41
|
+
serve_variation: "on";
|
|
42
|
+
rollout?: {
|
|
43
|
+
percentage: number;
|
|
44
|
+
attribute: "customerId";
|
|
45
|
+
};
|
|
46
|
+
}>;
|
|
47
|
+
}
|
|
48
|
+
export declare function booleanFlag(key: string, enabled?: boolean, ids?: string[], percentage?: number, description?: string): BooleanFlag;
|
|
49
|
+
/** Reject unsupported dashboard edits instead of erasing them. Provider metadata is excluded. */
|
|
50
|
+
export declare function supportedFlag(value: unknown): {
|
|
51
|
+
definition: BooleanFlag;
|
|
52
|
+
ids: string[];
|
|
53
|
+
percentage: number;
|
|
54
|
+
};
|
|
55
|
+
export type FlagAction = "create" | "enable" | "disable" | "audience" | "rollout";
|
|
56
|
+
export declare function changeFlag(current: unknown, action: FlagAction, input: {
|
|
57
|
+
key: string;
|
|
58
|
+
customerIds?: unknown;
|
|
59
|
+
percentage?: unknown;
|
|
60
|
+
all?: boolean;
|
|
61
|
+
}): Record<string, unknown>;
|
|
62
|
+
export declare function record(value: unknown): value is Record<string, unknown>;
|
|
63
|
+
//# sourceMappingURL=flagship-contract.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"flagship-contract.d.ts","sourceRoot":"","sources":["../src/flagship-contract.ts"],"names":[],"mappings":"AAAA,sFAAsF;AACtF,MAAM,MAAM,eAAe,GAAG,aAAa,GAAG,SAAS,GAAG,YAAY,CAAC;AACvE,MAAM,WAAW,WAAW;IAAG,OAAO,EAAE,MAAM,CAAC;IAAC,MAAM,EAAE,MAAM,CAAA;CAAE;AAChE,MAAM,WAAW,YAAY;IAC3B,QAAQ,EAAE,WAAW,EAAE,CAAC;IACxB,GAAG,EAAE;QAAE,OAAO,EAAE;YAAE,QAAQ,EAAE,WAAW,EAAE,CAAA;SAAE,CAAC;QAAC,UAAU,EAAE;YAAE,QAAQ,EAAE,WAAW,EAAE,CAAA;SAAE,CAAA;KAAE,CAAC;CACxF;AACD,wBAAgB,eAAe,CAAC,KAAK,EAAE,OAAO,GAAG,eAAe,CAG/D;AACD,wBAAgB,YAAY,CAAC,KAAK,EAAE,OAAO,GAAG,WAAW,EAAE,CAW1D;AACD,wBAAgB,iBAAiB,CAAC,KAAK,EAAE,OAAO,GAAG,YAAY,GAAG,SAAS,CAe1E;AACD,wBAAgB,oBAAoB,CAAC,KAAK,EAAE,YAAY,GAAG,SAAS,EAAE,WAAW,EAAE,eAAe,GAAG,WAAW,EAAE,CAEjH;AACD,wBAAgB,OAAO,CAAC,KAAK,EAAE,OAAO,GAAG,MAAM,CAG9C;AACD,wBAAgB,WAAW,CAAC,KAAK,EAAE,OAAO,GAAG,MAAM,EAAE,CAGpD;AACD,wBAAgB,iBAAiB,CAAC,KAAK,EAAE,OAAO,GAAG,MAAM,CAGxD;AACD,MAAM,WAAW,WAAW;IAC1B,GAAG,EAAE,MAAM,CAAC;IAAC,WAAW,CAAC,EAAE,MAAM,CAAC;IAAC,OAAO,EAAE,OAAO,CAAC;IAAC,iBAAiB,EAAE,KAAK,CAAC;IAC9E,UAAU,EAAE;QAAE,GAAG,EAAE,KAAK,CAAC;QAAC,EAAE,EAAE,IAAI,CAAA;KAAE,CAAC;IACrC,KAAK,EAAE,KAAK,CAAC;QAAE,QAAQ,EAAE,MAAM,CAAC;QAAC,UAAU,EAAE,KAAK,CAAC;YAAC,SAAS,EAAE,YAAY,CAAC;YAAC,QAAQ,EAAE,IAAI,CAAC;YAAC,KAAK,EAAE,MAAM,EAAE,CAAA;SAAC,CAAC,CAAC;QAC7G,eAAe,EAAE,IAAI,CAAC;QAAC,OAAO,CAAC,EAAE;YAAC,UAAU,EAAE,MAAM,CAAC;YAAC,SAAS,EAAE,YAAY,CAAA;SAAC,CAAA;KAAE,CAAC,CAAC;CACrF;AACD,wBAAgB,WAAW,CAAC,GAAG,EAAE,MAAM,EAAE,OAAO,UAAQ,EAAE,GAAG,GAAE,MAAM,EAAO,EAAE,UAAU,SAAI,EAAE,WAAW,CAAC,EAAE,MAAM,GAAG,WAAW,CAM/H;AACD,iGAAiG;AACjG,wBAAgB,aAAa,CAAC,KAAK,EAAE,OAAO,GAAG;IAAE,UAAU,EAAE,WAAW,CAAC;IAAC,GAAG,EAAE,MAAM,EAAE,CAAC;IAAC,UAAU,EAAE,MAAM,CAAA;CAAE,CAiB5G;AACD,MAAM,MAAM,UAAU,GAAG,QAAQ,GAAG,QAAQ,GAAG,SAAS,GAAG,UAAU,GAAG,SAAS,CAAC;AAClF,wBAAgB,UAAU,CAAC,OAAO,EAAE,OAAO,EAAE,MAAM,EAAE,UAAU,EAAE,KAAK,EAAE;IAAC,GAAG,EAAE,MAAM,CAAC;IAAC,WAAW,CAAC,EAAE,OAAO,CAAC;IAAC,UAAU,CAAC,EAAE,OAAO,CAAC;IAAC,GAAG,CAAC,EAAE,OAAO,CAAA;CAAC,GAAG,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAe1K;AACD,wBAAgB,MAAM,CAAC,KAAK,EAAE,OAAO,GAAG,KAAK,IAAI,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAA0E"}
|
|
@@ -0,0 +1,117 @@
|
|
|
1
|
+
export function flagEnvironment(value) {
|
|
2
|
+
if (value !== "development" && value !== "preview" && value !== "production")
|
|
3
|
+
throw new Error("flagship_environment_invalid");
|
|
4
|
+
return value;
|
|
5
|
+
}
|
|
6
|
+
export function flagBindings(value) {
|
|
7
|
+
if (!Array.isArray(value) || value.length > 8)
|
|
8
|
+
throw new Error("flagship_bindings_invalid");
|
|
9
|
+
const names = new Set();
|
|
10
|
+
return value.map(item => {
|
|
11
|
+
if (!record(item) || Object.keys(item).some(k => k !== "binding" && k !== "app_id")
|
|
12
|
+
|| typeof item.binding !== "string" || !/^[A-Z][A-Z0-9_]{2,63}$/.test(item.binding)
|
|
13
|
+
|| /^(TENDER_|ANDEO_)/.test(item.binding) || item.binding === "ASSETS" || names.has(item.binding)
|
|
14
|
+
|| typeof item.app_id !== "string" || !/^[A-Za-z0-9_-]{1,128}$/.test(item.app_id))
|
|
15
|
+
throw new Error("flagship_bindings_invalid");
|
|
16
|
+
names.add(item.binding);
|
|
17
|
+
return { binding: item.binding, app_id: item.app_id };
|
|
18
|
+
}).sort((a, b) => a.binding.localeCompare(b.binding));
|
|
19
|
+
}
|
|
20
|
+
export function parseFlagBindings(value) {
|
|
21
|
+
if (value === undefined)
|
|
22
|
+
return undefined;
|
|
23
|
+
if (!record(value) || Object.keys(value).some(k => !["flagship", "env"].includes(k))
|
|
24
|
+
|| !record(value.env) || Object.keys(value.env).some(k => !["preview", "production"].includes(k)))
|
|
25
|
+
throw new Error("flagship_config_invalid");
|
|
26
|
+
const environments = value.env;
|
|
27
|
+
const environment = (name) => {
|
|
28
|
+
const entry = environments[name];
|
|
29
|
+
if (!record(entry) || Object.keys(entry).some(k => k !== "flagship"))
|
|
30
|
+
throw new Error("flagship_environment_missing");
|
|
31
|
+
return { flagship: flagBindings(entry.flagship) };
|
|
32
|
+
};
|
|
33
|
+
const result = { flagship: flagBindings(value.flagship), env: { preview: environment("preview"), production: environment("production") } };
|
|
34
|
+
// Same names in every environment; explicit empty lists are supported only together.
|
|
35
|
+
const names = result.flagship.map(b => b.binding).join(",");
|
|
36
|
+
for (const entry of Object.values(result.env))
|
|
37
|
+
if (entry.flagship.map(b => b.binding).join(",") !== names)
|
|
38
|
+
throw new Error("flagship_environment_binding_mismatch");
|
|
39
|
+
return result;
|
|
40
|
+
}
|
|
41
|
+
export function selectedFlagBindings(value, environment) {
|
|
42
|
+
return value === undefined ? [] : environment === "development" ? value.flagship : value.env[environment].flagship;
|
|
43
|
+
}
|
|
44
|
+
export function flagKey(value) {
|
|
45
|
+
if (typeof value !== "string" || !/^[a-zA-Z0-9][a-zA-Z0-9._-]{0,127}$/.test(value))
|
|
46
|
+
throw new Error("flagship_key_invalid");
|
|
47
|
+
return value;
|
|
48
|
+
}
|
|
49
|
+
export function customerIds(value) {
|
|
50
|
+
if (!Array.isArray(value) || value.length > 1000 || value.some(id => typeof id !== "string" || !id.trim() || id !== id.trim() || id.length > 128 || /[\x00-\x1f\x7f]/.test(id)))
|
|
51
|
+
throw new Error("flagship_audience_invalid");
|
|
52
|
+
return [...new Set(value)].sort();
|
|
53
|
+
}
|
|
54
|
+
export function rolloutPercentage(value) {
|
|
55
|
+
if (typeof value !== "number" || !Number.isFinite(value) || value < 0 || value > 100 || Number(value.toFixed(2)) !== value)
|
|
56
|
+
throw new Error("flagship_percentage_invalid");
|
|
57
|
+
return value;
|
|
58
|
+
}
|
|
59
|
+
export function booleanFlag(key, enabled = false, ids = [], percentage = 0, description) {
|
|
60
|
+
ids = customerIds(ids);
|
|
61
|
+
percentage = rolloutPercentage(percentage);
|
|
62
|
+
return { key: flagKey(key), ...(description === undefined ? {} : { description }), enabled, default_variation: "off", variations: { off: false, on: true }, rules: [
|
|
63
|
+
...(ids.length ? [{ priority: 1, conditions: [{ attribute: "customerId", operator: "in", value: ids }], serve_variation: "on" }] : []),
|
|
64
|
+
...(percentage > 0 ? [{ priority: 2, conditions: [], serve_variation: "on", rollout: { percentage, attribute: "customerId" } }] : []),
|
|
65
|
+
] };
|
|
66
|
+
}
|
|
67
|
+
/** Reject unsupported dashboard edits instead of erasing them. Provider metadata is excluded. */
|
|
68
|
+
export function supportedFlag(value) {
|
|
69
|
+
if (!record(value) || typeof value.enabled !== "boolean" || value.default_variation !== "off" || !record(value.variations)
|
|
70
|
+
|| value.variations.off !== false || value.variations.on !== true || Object.keys(value.variations).length !== 2
|
|
71
|
+
|| !Array.isArray(value.rules) || value.rules.length > 2 || (value.description !== undefined && typeof value.description !== "string"))
|
|
72
|
+
throw new Error("flagship_reconciliation_required");
|
|
73
|
+
let ids = [], percentage = 0;
|
|
74
|
+
for (const rule of value.rules) {
|
|
75
|
+
if (!record(rule) || Object.keys(rule).some(k => !["priority", "conditions", "serve_variation", "rollout"].includes(k)) || rule.serve_variation !== "on" || !Array.isArray(rule.conditions))
|
|
76
|
+
throw new Error("flagship_reconciliation_required");
|
|
77
|
+
if (rule.priority === 1 && rule.conditions.length === 1 && rule.rollout === undefined && !ids.length) {
|
|
78
|
+
const condition = rule.conditions[0];
|
|
79
|
+
if (!record(condition) || Object.keys(condition).some(k => !["attribute", "operator", "value"].includes(k)) || condition.attribute !== "customerId" || condition.operator !== "in")
|
|
80
|
+
throw new Error("flagship_reconciliation_required");
|
|
81
|
+
ids = customerIds(condition.value);
|
|
82
|
+
if (!ids.length)
|
|
83
|
+
throw new Error("flagship_reconciliation_required");
|
|
84
|
+
}
|
|
85
|
+
else if (rule.priority === 2 && rule.conditions.length === 0 && record(rule.rollout) && percentage === 0
|
|
86
|
+
&& Object.keys(rule.rollout).every(k => ["percentage", "attribute"].includes(k)) && rule.rollout.attribute === "customerId") {
|
|
87
|
+
percentage = rolloutPercentage(rule.rollout.percentage);
|
|
88
|
+
if (percentage === 0)
|
|
89
|
+
throw new Error("flagship_reconciliation_required");
|
|
90
|
+
}
|
|
91
|
+
else
|
|
92
|
+
throw new Error("flagship_reconciliation_required");
|
|
93
|
+
}
|
|
94
|
+
return { definition: booleanFlag(flagKey(value.key), value.enabled, ids, percentage, value.description), ids, percentage };
|
|
95
|
+
}
|
|
96
|
+
export function changeFlag(current, action, input) {
|
|
97
|
+
if (action === "create")
|
|
98
|
+
return booleanFlag(input.key);
|
|
99
|
+
// Emergency disable preserves the complete observed writable definition, including unsupported rules.
|
|
100
|
+
if (action === "disable") {
|
|
101
|
+
if (!record(current) || !record(current.variations) || current.default_variation !== "off" || current.variations.off !== false
|
|
102
|
+
|| !Array.isArray(current.rules) || typeof current.enabled !== "boolean" || flagKey(current.key) !== input.key)
|
|
103
|
+
throw new Error("flagship_reconciliation_required");
|
|
104
|
+
return { key: input.key, ...(current.description === undefined ? {} : { description: current.description }), variations: current.variations,
|
|
105
|
+
default_variation: "off", rules: current.rules, enabled: false };
|
|
106
|
+
}
|
|
107
|
+
const { definition, ids, percentage } = supportedFlag(current);
|
|
108
|
+
if (definition.key !== input.key)
|
|
109
|
+
throw new Error("flagship_key_mismatch");
|
|
110
|
+
const nextIds = action === "audience" && !input.all ? customerIds(input.customerIds) : ids;
|
|
111
|
+
const nextPercentage = action === "rollout" ? rolloutPercentage(input.percentage) : action === "audience" && input.all ? 100 : percentage;
|
|
112
|
+
if (action === "enable" && !nextIds.length && nextPercentage === 0)
|
|
113
|
+
throw new Error("flagship_empty_audience");
|
|
114
|
+
return booleanFlag(input.key, action === "enable" ? true : definition.enabled, nextIds, nextPercentage, definition.description);
|
|
115
|
+
}
|
|
116
|
+
export function record(value) { return !!value && typeof value === "object" && !Array.isArray(value); }
|
|
117
|
+
//# sourceMappingURL=flagship-contract.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"flagship-contract.js","sourceRoot":"","sources":["../src/flagship-contract.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"flagship-package.d.ts","sourceRoot":"","sources":["../src/flagship-package.ts"],"names":[],"mappings":"AAOA,uFAAuF;AACvF,wBAAsB,mBAAmB,CAAC,IAAI,EAAC,MAAM,EAAC,iBAAiB,EAAC,MAAM,GAAE,OAAO,CAAC,IAAI,CAAC,CAiB5F"}
|
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
import { writeFile } from "node:fs/promises";
|
|
2
|
+
import { resolve } from "node:path";
|
|
3
|
+
import { parseFlagBindings, record } from "./flagship-contract.js";
|
|
4
|
+
import { verifyArtifact } from "./artifact.js";
|
|
5
|
+
import { pathExists, readBoundedFile } from "./files.js";
|
|
6
|
+
import { createHash } from "node:crypto";
|
|
7
|
+
/** Retain every environment in the artifact; only the trusted deployer selects one. */
|
|
8
|
+
export async function packageFlagBindings(root, artifactDirectory) {
|
|
9
|
+
const path = resolve(root, "tender-app.config.json");
|
|
10
|
+
if (!await pathExists(path))
|
|
11
|
+
return;
|
|
12
|
+
const source = JSON.parse(new TextDecoder().decode(await readBoundedFile(path, 1024 * 1024)));
|
|
13
|
+
if (!record(source))
|
|
14
|
+
throw new Error("flagship_source_invalid");
|
|
15
|
+
if (source.flagship === undefined && (!record(source.env) || !Object.values(source.env).some(e => record(e) && e.flagship !== undefined)))
|
|
16
|
+
return;
|
|
17
|
+
const flags = parseFlagBindings({ flagship: source.flagship, env: source.env });
|
|
18
|
+
const verified = await verifyArtifact(artifactDirectory);
|
|
19
|
+
if (verified.envelope.manifest.flags && JSON.stringify(verified.envelope.manifest.flags) !== JSON.stringify(flags))
|
|
20
|
+
throw new Error("flagship_declaration_mismatch");
|
|
21
|
+
const { envelope } = verified;
|
|
22
|
+
envelope.manifest.flags = flags;
|
|
23
|
+
const manifest = envelope.manifest;
|
|
24
|
+
envelope.bundleDigest = createHash("sha256").update(JSON.stringify({ module: manifest.module, files: manifest.files,
|
|
25
|
+
...(manifest.databaseSchema === undefined ? {} : { databaseSchema: manifest.databaseSchema }),
|
|
26
|
+
...(manifest.workflows === undefined ? {} : { workflows: manifest.workflows }), flags })).digest("hex");
|
|
27
|
+
manifest.runtime.assetPrefix = `/_tenant-artifacts/${envelope.bundleDigest}`;
|
|
28
|
+
await writeFile(verified.envelopePath, JSON.stringify(envelope, null, 2) + "\n");
|
|
29
|
+
await verifyArtifact(artifactDirectory);
|
|
30
|
+
}
|
|
31
|
+
//# sourceMappingURL=flagship-package.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"flagship-package.js","sourceRoot":"","sources":["../src/flagship-package.ts"],"names":[],"mappings":"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"}
|
package/dist/main.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"main.d.ts","sourceRoot":"","sources":["../src/main.ts"],"names":[],"mappings":"AASA,OAAO,EAIL,KAAK,YAAY,EAIlB,MAAM,iBAAiB,CAAC;AA2BzB,OAAO,EAIL,4BAA4B,EAE7B,MAAM,YAAY,CAAC;AACpB,OAAO,EAAE,cAAc,EAAE,iBAAiB,EAAE,MAAM,gBAAgB,CAAC;AACnE,OAAO,EAEL,KAAK,kBAAkB,EACxB,MAAM,uBAAuB,CAAC;
|
|
1
|
+
{"version":3,"file":"main.d.ts","sourceRoot":"","sources":["../src/main.ts"],"names":[],"mappings":"AASA,OAAO,EAIL,KAAK,YAAY,EAIlB,MAAM,iBAAiB,CAAC;AA2BzB,OAAO,EAIL,4BAA4B,EAE7B,MAAM,YAAY,CAAC;AACpB,OAAO,EAAE,cAAc,EAAE,iBAAiB,EAAE,MAAM,gBAAgB,CAAC;AACnE,OAAO,EAEL,KAAK,kBAAkB,EACxB,MAAM,uBAAuB,CAAC;AAW/B,eAAO,MAAM,WAAW,QAA0B,CAAC;AAenD,MAAM,WAAW,UAAU;IACzB,aAAa,EAAE,MAAM,CAAC;IACtB,GAAG,EAAE,MAAM,CAAC;IACZ,aAAa,EAAE,MAAM,CAAC;IACtB,WAAW,EAAE,MAAM,CAAC,UAAU,CAAC;IAC/B,KAAK,EAAE,MAAM,CAAC,cAAc,CAAC;IAC7B,MAAM,EAAE,IAAI,CAAC,MAAM,CAAC,WAAW,EAAE,OAAO,CAAC,CAAC;IAC1C,MAAM,EAAE,IAAI,CAAC,MAAM,CAAC,WAAW,EAAE,OAAO,CAAC,CAAC;IAC1C,OAAO,EAAE,OAAO,KAAK,CAAC;IACtB,KAAK,CAAC,YAAY,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC,CAAC;IAC3C,iBAAiB,EAAE,OAAO,iBAAiB,CAAC;IAC5C,cAAc,EAAE,OAAO,cAAc,CAAC;IACtC,SAAS,EAAE,YAAY,CAAC;IACxB,eAAe,EAAE,kBAAkB,CAAC;IACpC,WAAW,CAAC,GAAG,EAAE,MAAM,GAAG,OAAO,CAAC,OAAO,CAAC,CAAC;IAC3C,4BAA4B,EAAE,OAAO,4BAA4B,CAAC;IAClE,aAAa,CAAC,GAAG,EAAE,MAAM,EAAE,SAAS,EAAE,MAAM,EAAE,GAAG,gBAAgB,CAAC;IAClE,WAAW,CAAC,OAAO,EAAE,MAAM,IAAI,GAAG,MAAM,IAAI,CAAC;CAC9C;AAED,MAAM,WAAW,gBAAgB;IAC/B,gBAAgB,CAAC,IAAI,EAAE,MAAM,EAAE,QAAQ,EAAE,MAAM,IAAI,GAAG,IAAI,CAAC;IAC3D,gBAAgB,CAAC,IAAI,EAAE,SAAS,EAAE,QAAQ,EAAE,CAAC,KAAK,EAAE;QAAE,IAAI,EAAE,OAAO,CAAA;KAAE,KAAK,IAAI,GAAG,IAAI,CAAC;IACtF,gBAAgB,CAAC,IAAI,EAAE,OAAO,EAAE,QAAQ,EAAE,CAAC,KAAK,EAAE;QAAE,IAAI,EAAE,MAAM,CAAC;QAAC,MAAM,EAAE,MAAM,CAAA;KAAE,KAAK,IAAI,GAAG,IAAI,CAAC;IACnG,gBAAgB,CAAC,IAAI,EAAE,OAAO,EAAE,QAAQ,EAAE,MAAM,IAAI,GAAG,IAAI,CAAC;IAC5D,KAAK,CAAC,IAAI,CAAC,EAAE,MAAM,EAAE,MAAM,CAAC,EAAE,MAAM,GAAG,IAAI,CAAC;CAC7C;AA6BD,wBAAsB,MAAM,CAAC,IAAI,EAAE,MAAM,EAAE,EAAE,SAAS,GAAE,OAAO,CAAC,UAAU,CAAM,GAAG,OAAO,CAAC,MAAM,CAAC,CAmCjG"}
|
package/dist/main.js
CHANGED
|
@@ -20,6 +20,7 @@ import { initializeStarter } from "./starter.js";
|
|
|
20
20
|
import { normalizeAndeoEnvironment } from "./andeo-environment.js";
|
|
21
21
|
import { DOC_TOPICS, readDocumentation, installDocumentation, installedSkillCheck } from "./documentation.js";
|
|
22
22
|
import { capabilityPreflight } from "./capability-preflight.js";
|
|
23
|
+
import { flagEnvironment, flagKey, rolloutPercentage, customerIds } from "./flagship-contract.js";
|
|
23
24
|
const packageMetadata = createRequire(import.meta.url)("../package.json");
|
|
24
25
|
if (typeof packageMetadata.version !== "string" || packageMetadata.version.length === 0) {
|
|
25
26
|
throw new Error("Andeo CLI package version is missing.");
|
|
@@ -101,6 +102,8 @@ async function execute(topic, options, cwd, runtime) {
|
|
|
101
102
|
return { value: await workflowCommand(topic.slice(10), cwd, options, runtime) };
|
|
102
103
|
if (topic.startsWith("email "))
|
|
103
104
|
return { value: await emailCommand(topic.slice(6), cwd, options, runtime) };
|
|
105
|
+
if (topic.startsWith("flagship "))
|
|
106
|
+
return { value: await flagshipCommand(topic.slice(9), cwd, options, runtime) };
|
|
104
107
|
switch (topic) {
|
|
105
108
|
case "docs": {
|
|
106
109
|
const name = options.positionals[0];
|
|
@@ -225,7 +228,27 @@ async function execute(topic, options, cwd, runtime) {
|
|
|
225
228
|
return { value: await sourceChangeCreate(cwd, options, runtime) };
|
|
226
229
|
case "source merge": {
|
|
227
230
|
const value = await sourceMerge(cwd, options, runtime);
|
|
228
|
-
return { value, exitCode: value.status === "timeout" ? 3 : value.merge && ["awaiting_approval", "needs_attention", "superseded", "authorization_required", "cancelled"].includes(value.merge.stage) ? 2 : 0 };
|
|
231
|
+
return { value, exitCode: value.status === "timeout" ? 3 : value.merge && ["awaiting_approval", "needs_attention", "superseded", "authorization_required", "reconciliation_required", "cancelled"].includes(value.merge.stage) ? 2 : 0 };
|
|
232
|
+
}
|
|
233
|
+
case "source merge status":
|
|
234
|
+
case "source merge resume":
|
|
235
|
+
case "source merge cancel":
|
|
236
|
+
case "source merge take-over": {
|
|
237
|
+
const mergeId = requiredFlag(options, "merge");
|
|
238
|
+
if (!/^smi_[A-Za-z0-9_-]+$/.test(mergeId))
|
|
239
|
+
throw new CliError("argument_invalid", "--merge must be a merge intent ID (smi_...).");
|
|
240
|
+
const key = topic === "source merge take-over" ? requiredFlag(options, "idempotency-key") : undefined;
|
|
241
|
+
if (key && !/^[A-Za-z0-9:_-]{8,128}$/.test(key))
|
|
242
|
+
throw new CliError("argument_invalid", "--idempotency-key must contain 8–128 letters, numbers, colons, underscores or hyphens.");
|
|
243
|
+
const access = await sourceReadAccess(cwd, options, runtime);
|
|
244
|
+
const current = await access.client.sourceMergeStatus(mergeId);
|
|
245
|
+
requireLinkedSourceChange(access.projectId, current.merge.projectId);
|
|
246
|
+
if (current.merge.id !== mergeId || current.merge.repositoryId !== access.repository.id)
|
|
247
|
+
throw new CliError("source_merge_changed", "The returned merge does not match this repository and intent.");
|
|
248
|
+
if (topic === "source merge status")
|
|
249
|
+
return { value: current };
|
|
250
|
+
return { value: topic === "source merge resume" ? await access.client.resumeSourceMergeById(mergeId)
|
|
251
|
+
: key ? await access.client.takeOverSourceMergeById(mergeId, key) : await access.client.cancelSourceMergeById(mergeId) };
|
|
229
252
|
}
|
|
230
253
|
case "source change resume": {
|
|
231
254
|
const access = await sourceReadAccess(cwd, options, runtime);
|
|
@@ -249,13 +272,17 @@ async function execute(topic, options, cwd, runtime) {
|
|
|
249
272
|
if (topic === "source merge-policy show")
|
|
250
273
|
return { value: await access.client.sourceMergePolicy(access.repository.id) };
|
|
251
274
|
const mode = requiredFlag(options, "mode");
|
|
252
|
-
if (!["human-review", "agent"].includes(mode))
|
|
253
|
-
throw new CliError("source_merge_policy_invalid", "--mode must be human-review or
|
|
275
|
+
if (!["human-review", "agent", "administrator-authorized"].includes(mode))
|
|
276
|
+
throw new CliError("source_merge_policy_invalid", "--mode must be human-review, agent or administrator-authorized.");
|
|
277
|
+
const principalIds = (options.flags.get("principals") ?? "").split(",").filter(Boolean);
|
|
278
|
+
if (mode === "administrator-authorized" && principalIds.length)
|
|
279
|
+
throw new CliError("source_merge_policy_invalid", "Administrator-authorized mode requires exact per-operation approval; omit --principals.");
|
|
254
280
|
const executionSeconds = options.flags.has("execution-seconds") ? Number(options.flags.get("execution-seconds")) : undefined;
|
|
255
281
|
if (executionSeconds !== undefined && (!Number.isSafeInteger(executionSeconds) || executionSeconds < 300 || executionSeconds > 86400))
|
|
256
282
|
throw new CliError("argument_invalid", "--execution-seconds must be between 300 and 86400.");
|
|
257
283
|
return { value: await access.client.sourceMergePolicy(access.repository.id, { enabled: mode === "agent",
|
|
258
|
-
principalIds:
|
|
284
|
+
principalIds, validationMode: mode === "administrator-authorized" ? "administrator_authorized" : "native_preview",
|
|
285
|
+
buildAdmission: mode === "administrator-authorized" ? "source_only" : "native",
|
|
259
286
|
expectedRevision: Number(requiredFlag(options, "expected-revision")), idempotencyKey: requiredFlag(options, "idempotency-key"), executionSeconds }) };
|
|
260
287
|
}
|
|
261
288
|
case "source change list":
|
|
@@ -1329,9 +1356,34 @@ async function sourceMerge(cwd, options, runtime) {
|
|
|
1329
1356
|
throw new CliError("argument_invalid", "--expected-head must be a commit SHA.");
|
|
1330
1357
|
if (expectedSnapshot && (!changeId || !Number.isSafeInteger(Number(expectedSnapshot)) || Number(expectedSnapshot) < 1))
|
|
1331
1358
|
throw new CliError("argument_invalid", "--expected-snapshot requires --change and a positive integer.");
|
|
1359
|
+
const expectedBase = options.flags.get("expected-base"), policyRevision = options.flags.get("expected-policy-revision");
|
|
1360
|
+
const validationMode = options.flags.get("validation-mode"), continuation = options.flags.get("continuation");
|
|
1361
|
+
const authorize = options.booleans.has("authorize"), dryRun = options.booleans.has("dry-run"), note = options.flags.get("note");
|
|
1362
|
+
if (expectedBase && !/^[a-f0-9]{40}$/.test(expectedBase))
|
|
1363
|
+
throw new CliError("argument_invalid", "--expected-base must be a commit SHA.");
|
|
1364
|
+
if (policyRevision !== undefined && (!/^\d+$/.test(policyRevision) || !Number.isSafeInteger(Number(policyRevision))))
|
|
1365
|
+
throw new CliError("argument_invalid", "--expected-policy-revision must be a nonnegative integer.");
|
|
1366
|
+
if (validationMode !== undefined && validationMode !== "native_preview" && validationMode !== "administrator_authorized")
|
|
1367
|
+
throw new CliError("argument_invalid", "--validation-mode must be native_preview or administrator_authorized.");
|
|
1368
|
+
if (continuation !== undefined && continuation !== "source_only")
|
|
1369
|
+
throw new CliError("argument_invalid", "--continuation must be source_only.");
|
|
1370
|
+
if (note !== undefined && (note.length < 1 || note.length > 1000))
|
|
1371
|
+
throw new CliError("argument_invalid", "--note must contain 1–1000 characters.");
|
|
1372
|
+
if (validationMode === "administrator_authorized") {
|
|
1373
|
+
if (changeId || expectedSnapshot)
|
|
1374
|
+
throw new CliError("argument_invalid", "Administrator-authorized merges use --head and --title without a native change request.");
|
|
1375
|
+
if (!dryRun && (!authorize || !expectedBase || !expectedHead || policyRevision === undefined))
|
|
1376
|
+
throw new CliError("argument_invalid", "Administrator-authorized merges require --authorize, --expected-base, --expected-head and --expected-policy-revision. Use --dry-run to inspect the exact snapshot first.");
|
|
1377
|
+
}
|
|
1378
|
+
else if (authorize || expectedBase !== undefined || policyRevision !== undefined || continuation !== undefined || note !== undefined) {
|
|
1379
|
+
throw new CliError("argument_invalid", "Administrator authorization flags require --validation-mode administrator_authorized.");
|
|
1380
|
+
}
|
|
1332
1381
|
const access = await sourceReadAccess(cwd, options, runtime);
|
|
1333
1382
|
const result = await access.client.sourceMerge(access.repository.id, { headBranch, title, changeId, idempotencyKey,
|
|
1334
|
-
expectedHead, expectedSnapshot: expectedSnapshot ? Number(expectedSnapshot) : undefined, dryRun
|
|
1383
|
+
expectedHead, expectedSnapshot: expectedSnapshot ? Number(expectedSnapshot) : undefined, dryRun,
|
|
1384
|
+
expectedBase, expectedPolicyRevision: policyRevision === undefined ? undefined : Number(policyRevision),
|
|
1385
|
+
validationMode, authorize: validationMode === "administrator_authorized" ? authorize : undefined,
|
|
1386
|
+
continuation: validationMode === "administrator_authorized" ? "source_only" : undefined, note });
|
|
1335
1387
|
if (result.merge) {
|
|
1336
1388
|
requireLinkedSourceChange(access.projectId, result.merge.projectId);
|
|
1337
1389
|
if (result.merge.repositoryId !== access.repository.id)
|
|
@@ -1341,13 +1393,14 @@ async function sourceMerge(cwd, options, runtime) {
|
|
|
1341
1393
|
return result;
|
|
1342
1394
|
let merge = result.merge;
|
|
1343
1395
|
for (let poll = 0; poll < Math.ceil(timeout / 5); poll++) {
|
|
1344
|
-
if (["deployed", "needs_attention", "publication_not_enabled", "superseded", "authorization_required", "cancelled"].includes(merge.stage))
|
|
1396
|
+
if (merge.terminal || ["merged", "deployed", "needs_attention", "publication_not_enabled", "superseded", "authorization_required", "reconciliation_required", "cancelled"].includes(merge.stage))
|
|
1345
1397
|
return { ...result, merge };
|
|
1346
1398
|
if (merge.stage === "awaiting_approval")
|
|
1347
1399
|
return { ...result, merge };
|
|
1348
1400
|
await runtime.sleep(Math.min(5000, timeout * 1000 - poll * 5000));
|
|
1349
|
-
const current = await access.client.sourceChangeRequest(merge.changeId);
|
|
1350
|
-
|
|
1401
|
+
const current = merge.changeId ? await access.client.sourceChangeRequest(merge.changeId) : await access.client.sourceMergeStatus(merge.id);
|
|
1402
|
+
if ("changeRequest" in current)
|
|
1403
|
+
requireLinkedSourceChange(access.projectId, current.changeRequest.projectId);
|
|
1351
1404
|
if (!current.merge || current.merge.id !== merge.id)
|
|
1352
1405
|
throw new CliError("source_merge_changed", "The merge intent changed. Inspect this exact change before continuing.");
|
|
1353
1406
|
requireLinkedSourceChange(access.projectId, current.merge.projectId);
|
|
@@ -1355,7 +1408,10 @@ async function sourceMerge(cwd, options, runtime) {
|
|
|
1355
1408
|
throw new CliError("source_merge_changed", "The returned merge belongs to another repository.");
|
|
1356
1409
|
merge = current.merge;
|
|
1357
1410
|
}
|
|
1358
|
-
|
|
1411
|
+
if (merge.terminal || ["merged", "deployed", "awaiting_approval", "needs_attention", "publication_not_enabled", "superseded", "authorization_required", "reconciliation_required", "cancelled"].includes(merge.stage))
|
|
1412
|
+
return { ...result, merge };
|
|
1413
|
+
return { ...result, status: "timeout", merge: { ...merge, waitTimedOut: true }, next: merge.changeId
|
|
1414
|
+
? `andeo source change show --change ${merge.changeId} --json` : `andeo source merge status --merge ${merge.id} --json` };
|
|
1359
1415
|
}
|
|
1360
1416
|
async function sourceChangeCreate(cwd, options, runtime) {
|
|
1361
1417
|
const access = await sourceReadAccess(cwd, options, runtime);
|
|
@@ -2410,7 +2466,82 @@ function exampleConfig() {
|
|
|
2410
2466
|
returnPath: "/account",
|
|
2411
2467
|
};
|
|
2412
2468
|
}
|
|
2469
|
+
async function flagshipCommand(command, cwd, options, runtime) {
|
|
2470
|
+
let environment;
|
|
2471
|
+
try {
|
|
2472
|
+
environment = flagEnvironment(requiredFlag(options, "env"));
|
|
2473
|
+
}
|
|
2474
|
+
catch {
|
|
2475
|
+
throw new CliError("flagship_environment_invalid", "Use --env development, preview, or production explicitly.");
|
|
2476
|
+
}
|
|
2477
|
+
const context = await optionalContext(cwd);
|
|
2478
|
+
const projectId = options.flags.get("project") ?? context?.link?.projectId;
|
|
2479
|
+
if (!projectId)
|
|
2480
|
+
throw new CliError("project_required", "Link a project or pass --project.");
|
|
2481
|
+
const { client } = await authenticatedClient(cwd, context?.link ?? null, options, runtime);
|
|
2482
|
+
if (command.startsWith("apps "))
|
|
2483
|
+
return client.flagship(projectId, ["apps", ...(command === "apps ensure" ? ["ensure"] : [])], environment, command === "apps ensure" ? { body: {} } : {});
|
|
2484
|
+
const action = command.slice(6), app = options.positionals[0], key = options.positionals[1];
|
|
2485
|
+
const segments = ["flags", app, ...(key ? [flagKey(key)] : [])];
|
|
2486
|
+
if (action === "list" || action === "get")
|
|
2487
|
+
return client.flagship(projectId, segments, environment);
|
|
2488
|
+
segments.push(action);
|
|
2489
|
+
if (action === "changelog")
|
|
2490
|
+
return client.flagship(projectId, segments, environment);
|
|
2491
|
+
if (action === "evaluate") {
|
|
2492
|
+
const context = requiredFlag(options, "context");
|
|
2493
|
+
if (!context.startsWith("customerId="))
|
|
2494
|
+
throw new CliError("flagship_context_invalid", "Use --context customerId=IDENTIFIER.");
|
|
2495
|
+
const [customerId] = customerIds([context.slice(11)]);
|
|
2496
|
+
return client.flagship(projectId, segments, environment, { customerId });
|
|
2497
|
+
}
|
|
2498
|
+
const revision = options.flags.get("expected-revision");
|
|
2499
|
+
// Read before mutation by default. Server rechecks this revision inside serialization.
|
|
2500
|
+
let expectedRevision;
|
|
2501
|
+
if (revision !== undefined) {
|
|
2502
|
+
expectedRevision = Number(revision);
|
|
2503
|
+
if (!/^\d+$/.test(revision) || !Number.isSafeInteger(expectedRevision))
|
|
2504
|
+
throw new CliError("flagship_revision_invalid", "Expected revision must be a nonnegative integer.");
|
|
2505
|
+
}
|
|
2506
|
+
else if (action === "create")
|
|
2507
|
+
expectedRevision = 0;
|
|
2508
|
+
else
|
|
2509
|
+
expectedRevision = (await client.flagship(projectId, segments.slice(0, -1), environment)).revision;
|
|
2510
|
+
const body = { expectedRevision };
|
|
2511
|
+
if (action === "audience") {
|
|
2512
|
+
const all = options.booleans.has("all"), file = options.flags.get("customer-ids-file");
|
|
2513
|
+
if (all === !!file)
|
|
2514
|
+
throw new CliError("flagship_audience_invalid", "Choose --all or --customer-ids-file.");
|
|
2515
|
+
if (all)
|
|
2516
|
+
body.all = true;
|
|
2517
|
+
else {
|
|
2518
|
+
let value;
|
|
2519
|
+
try {
|
|
2520
|
+
value = JSON.parse(new TextDecoder().decode(await readBoundedFile(resolve(cwd, file), 192 * 1024)));
|
|
2521
|
+
}
|
|
2522
|
+
catch {
|
|
2523
|
+
throw new CliError("flagship_audience_invalid", "The customer ID file must contain a bounded JSON array.");
|
|
2524
|
+
}
|
|
2525
|
+
body.customerIds = customerIds(value);
|
|
2526
|
+
}
|
|
2527
|
+
}
|
|
2528
|
+
if (action === "rollout") {
|
|
2529
|
+
if (requiredFlag(options, "to") !== "on" || requiredFlag(options, "by") !== "customerId")
|
|
2530
|
+
throw new CliError("flagship_rollout_invalid", "V1 supports --to on --by customerId.");
|
|
2531
|
+
const percentage = requiredFlag(options, "percentage");
|
|
2532
|
+
if (!/^(?:\d+)(?:\.\d{1,2})?$/.test(percentage))
|
|
2533
|
+
throw new CliError("flagship_percentage_invalid", "Use a percentage from 0 to 100 with at most two decimals.");
|
|
2534
|
+
body.percentage = rolloutPercentage(Number(percentage));
|
|
2535
|
+
}
|
|
2536
|
+
return client.flagship(projectId, segments, environment, { body });
|
|
2537
|
+
}
|
|
2413
2538
|
function commandTopic(argv) {
|
|
2539
|
+
if (argv[0] === "flagship") {
|
|
2540
|
+
const topic = argv.slice(0, 3).join(" ");
|
|
2541
|
+
if (argv.length < 3 || argv.includes("--help") || argv.includes("-h"))
|
|
2542
|
+
return { topic: "help", values: ["flagship"] };
|
|
2543
|
+
return { topic, values: argv.slice(3) };
|
|
2544
|
+
}
|
|
2414
2545
|
if (argv.length === 0 || argv[0] === "help" || argv[0] === "--help" || argv[0] === "-h") {
|
|
2415
2546
|
return { topic: "help", values: argv.slice(1) };
|
|
2416
2547
|
}
|
|
@@ -2425,6 +2556,10 @@ function commandTopic(argv) {
|
|
|
2425
2556
|
const child = argv[1];
|
|
2426
2557
|
if (!child || child.startsWith("--"))
|
|
2427
2558
|
return { topic: "help", values: [parent] };
|
|
2559
|
+
if (parent === "source" && child === "merge" && argv[2] && !argv[2].startsWith("-")) {
|
|
2560
|
+
const topic = `source merge ${argv[2]}`;
|
|
2561
|
+
return argv.includes("--help") || argv.includes("-h") ? { topic: "help", values: [topic] } : { topic, values: argv.slice(3) };
|
|
2562
|
+
}
|
|
2428
2563
|
if (parent === "source" && (child === "default" || child === "history" || child === "operation" || child === "change" || child === "policy" || child === "merge-policy" || child === "reconnect" || child === "branch")) {
|
|
2429
2564
|
const grandchild = argv[2];
|
|
2430
2565
|
if (!grandchild || grandchild.startsWith("--"))
|
|
@@ -2460,7 +2595,7 @@ function parseOptions(values) {
|
|
|
2460
2595
|
const booleans = new Set();
|
|
2461
2596
|
const positionals = [];
|
|
2462
2597
|
const booleanNames = new Set([
|
|
2463
|
-
"json", "token-stdin", "dry-run", "no-wait", "force", "reauthorize",
|
|
2598
|
+
"json", "token-stdin", "dry-run", "no-wait", "force", "reauthorize", "authorize",
|
|
2464
2599
|
"device", "wait", "no-open", "local", "all", "production",
|
|
2465
2600
|
]);
|
|
2466
2601
|
for (let index = 0; index < values.length; index += 1) {
|
|
@@ -2490,6 +2625,20 @@ function parseOptions(values) {
|
|
|
2490
2625
|
return { flags, booleans, positionals, json: booleans.has("json") };
|
|
2491
2626
|
}
|
|
2492
2627
|
function assertAllowedOptions(topic, options) {
|
|
2628
|
+
if (topic.startsWith("flagship ")) {
|
|
2629
|
+
const common = ["cwd", "json", "api-url", "token-stdin", "profile", "project", "env"];
|
|
2630
|
+
const extras = { "flags create": ["expected-revision"], "flags enable": ["expected-revision"], "flags disable": ["expected-revision"],
|
|
2631
|
+
"flags audience": ["expected-revision", "customer-ids-file", "all"], "flags rollout": ["expected-revision", "to", "percentage", "by"], "flags evaluate": ["context"], "flags reconcile": ["expected-revision"] };
|
|
2632
|
+
const sub = topic.slice(9);
|
|
2633
|
+
if (!["apps ensure", "apps list", "flags list", "flags get", "flags create", "flags enable", "flags disable", "flags audience", "flags rollout", "flags evaluate", "flags changelog", "flags reconcile"].includes(sub))
|
|
2634
|
+
throw new CliError("argument_unknown", "Unknown flagship command.");
|
|
2635
|
+
if ([...options.flags.keys(), ...options.booleans].some(k => ![...common, ...(extras[sub] ?? [])].includes(k)))
|
|
2636
|
+
throw new CliError("argument_unknown", "Unsupported flagship option.");
|
|
2637
|
+
const length = sub.startsWith("apps") ? 0 : sub === "flags list" ? 1 : 2;
|
|
2638
|
+
if (options.positionals.length !== length)
|
|
2639
|
+
throw new CliError("argument_required", `Expected ${length} positional arguments for ${topic}.`);
|
|
2640
|
+
return;
|
|
2641
|
+
}
|
|
2493
2642
|
const common = ["cwd", "json"];
|
|
2494
2643
|
const authentication = ["api-url", "token-stdin", "profile"];
|
|
2495
2644
|
const allowed = {
|
|
@@ -2588,7 +2737,12 @@ function assertAllowedOptions(topic, options) {
|
|
|
2588
2737
|
"source operation status": [...common, ...authentication, "operation"],
|
|
2589
2738
|
"source operation retry": [...common, ...authentication, "operation", "no-wait", "timeout-seconds"],
|
|
2590
2739
|
"source change create": [...common, ...authentication, "project", "title", "description", "base", "head"],
|
|
2591
|
-
"source merge": [...common, ...authentication, "project", "head", "title", "change", "expected-head", "expected-snapshot", "idempotency-key", "dry-run", "wait", "no-wait", "timeout-seconds"
|
|
2740
|
+
"source merge": [...common, ...authentication, "project", "head", "title", "change", "expected-head", "expected-snapshot", "idempotency-key", "dry-run", "wait", "no-wait", "timeout-seconds",
|
|
2741
|
+
"expected-base", "expected-policy-revision", "validation-mode", "authorize", "continuation", "note"],
|
|
2742
|
+
"source merge status": [...common, ...authentication, "project", "merge"],
|
|
2743
|
+
"source merge resume": [...common, ...authentication, "project", "merge"],
|
|
2744
|
+
"source merge cancel": [...common, ...authentication, "project", "merge"],
|
|
2745
|
+
"source merge take-over": [...common, ...authentication, "project", "merge", "idempotency-key"],
|
|
2592
2746
|
"source change resume": [...common, ...authentication, "project", "change"],
|
|
2593
2747
|
"source change cancel": [...common, ...authentication, "project", "change"],
|
|
2594
2748
|
"source change take-over": [...common, ...authentication, "project", "change", "idempotency-key"],
|
|
@@ -2726,6 +2880,8 @@ function writeOutput(value, options, runtime) {
|
|
|
2726
2880
|
runtime.stdout.write(`${JSON.stringify(value, null, 2)}\n`);
|
|
2727
2881
|
}
|
|
2728
2882
|
function helpText(topic) {
|
|
2883
|
+
if (topic === "flagship")
|
|
2884
|
+
return "Usage: andeo flagship apps ensure|list --env development|preview|production [--project ID] [--json]\n andeo flagship flags list APP_ID --env ENV\n andeo flagship flags create|get|enable|disable|changelog|reconcile APP_ID KEY --env ENV [--expected-revision N]\n andeo flagship flags audience APP_ID KEY --env ENV --customer-ids-file FILE|--all\n andeo flagship flags rollout APP_ID KEY --env ENV --to on --percentage 5 --by customerId\n andeo flagship flags evaluate APP_ID KEY --env ENV --context customerId=synthetic-customer-a\n\nDefaults off. Audience and percentage edits preserve each other and do not enable a disabled flag. Changes propagate; pending writes must be reconciled.";
|
|
2729
2885
|
if (topic === "email" || topic?.startsWith("email "))
|
|
2730
2886
|
return `Usage: andeo email <capabilities|create|list|cursor|wait|read|messages|retire> [options]
|
|
2731
2887
|
|
|
@@ -2805,11 +2961,15 @@ Account workflow entitlement is required for new starts. Run status is authorita
|
|
|
2805
2961
|
"source change": "Usage: andeo source change <create|list|show|close|resume|cancel|take-over> [options]\n\nCreate and inspect exact preview-backed changes, or recover an accepted merge intent.",
|
|
2806
2962
|
"source change cancel": "Usage: andeo source change cancel --change scr_... [--json]\n\nWithdraw new work for an accepted intent. Already-issued effects require readback; source may already be merged.",
|
|
2807
2963
|
"source change take-over": "Usage: andeo source change take-over --change scr_... --idempotency-key KEY [--json]\n\nAdministrator only. Records a new bounded authorization for the same exact inputs; then use source change resume.",
|
|
2808
|
-
"source merge": "Usage: andeo source merge --head BRANCH --title TITLE --idempotency-key KEY [--dry-run] [--wait] [--timeout-seconds N] [--json]\n andeo source merge --change scr_... --idempotency-key KEY [--wait] [--json]\n\nRequest exact fast-forward landing
|
|
2964
|
+
"source merge": "Usage: andeo source merge --head BRANCH --title TITLE --idempotency-key KEY [--dry-run] [--wait] [--timeout-seconds N] [--json]\n andeo source merge --change scr_... --idempotency-key KEY [--wait] [--json]\n andeo source merge --head BRANCH --title TITLE --validation-mode administrator_authorized --expected-base SHA --expected-head SHA --expected-policy-revision N --authorize --idempotency-key KEY [--continuation source_only] [--note TEXT] [--wait] [--json]\n andeo source merge <status|resume|cancel|take-over> --merge smi_... [options]\n\nRequest exact fast-forward landing. Native review remains the default. Administrator-authorized source-only mode requires saved policy and a current administrator; Andeo does not independently verify checks. Inspect refs and blockers with --dry-run before authorizing. Waiting is bounded and never cancels hosted work.\n\nExamples:\n andeo source merge --head feature/copy --title \"Update copy\" --idempotency-key account-copy-v1 --dry-run --json\n andeo source merge --head feature/copy --title \"Update copy\" --idempotency-key account-copy-v1 --wait --json",
|
|
2965
|
+
"source merge status": "Usage: andeo source merge status --merge smi_... [--json]\n\nRead the retained source, validation, authorization, continuation and cleanup outcomes. Source-only merged is successful completion.",
|
|
2966
|
+
"source merge resume": "Usage: andeo source merge resume --merge smi_... [--json]\n\nResume the same exact eligible intent after revalidating authority. Never substitutes a new source snapshot.",
|
|
2967
|
+
"source merge cancel": "Usage: andeo source merge cancel --merge smi_... [--json]\n\nWithdraw new work. Already-issued effects require readback; cancellation cannot undo a merge.",
|
|
2968
|
+
"source merge take-over": "Usage: andeo source merge take-over --merge smi_... --idempotency-key KEY [--json]\n\nAdministrator only. Take ownership of the retained exact source snapshot before resuming.",
|
|
2809
2969
|
"source merge-policy": "Usage: andeo source merge-policy <show|set> [options]",
|
|
2810
2970
|
"source change resume": "Usage: andeo source change resume --change scr_... [--json]\n\nResume the same eligible merge intent or exact failed publication. Revalidates authority and retained lineage; never creates a replacement build.\n\nExample:\n andeo source change resume --change scr_... --json",
|
|
2811
2971
|
"source merge-policy show": "Usage: andeo source merge-policy show [--json]\n\nRead the saved merge policy and explicit principal grants.",
|
|
2812
|
-
"source merge-policy set": "Usage: andeo source merge-policy set --mode human-review|agent --principals PRINCIPAL_IDS --expected-revision N --idempotency-key KEY [--json]\n\nAdministrator only.
|
|
2972
|
+
"source merge-policy set": "Usage: andeo source merge-policy set --mode human-review|agent|administrator-authorized [--principals PRINCIPAL_IDS] --expected-revision N --idempotency-key KEY [--json]\n\nAdministrator only. Agent grants are comma-separated stable principal IDs. Administrator-authorized mode requires controlled transport, source-only continuation and exact per-operation approval; omit --principals. This never changes publication policy.\n\nExample:\n andeo source merge-policy set --mode agent --principals principal_example --expected-revision 0 --idempotency-key merge-policy-v1 --json",
|
|
2813
2973
|
"source change create": "Usage: andeo source change create --head BRANCH --title TITLE [--description TEXT] [--base BRANCH] [--json]\n\nOpen a review for the exact protected-base and feature-branch SHAs. Andeo binds checks and approval to this immutable snapshot.",
|
|
2814
2974
|
"source change list": "Usage: andeo source change list [--state open|closed|all] [--json]\n\nList reviewed source changes for this exact linked project.",
|
|
2815
2975
|
"source change show": "Usage: andeo source change show --change scr_... [--json]\n\nRead exact snapshot, preview, decision, and landing state.",
|