@withandeo/cli 0.19.8 → 0.19.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 +44 -3
- 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 +13 -3
- package/dist/documentation.d.ts.map +1 -1
- package/dist/documentation.js +108 -12
- 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 +177 -17
- package/dist/main.js.map +1 -1
- package/dist/public-skill-contract.d.ts +21 -0
- package/dist/public-skill-contract.d.ts.map +1 -0
- package/dist/public-skill-contract.js +47 -0
- package/dist/public-skill-contract.js.map +1 -0
- 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/publication.json +3 -0
- package/skill/tender-accounts/SKILL.md +3 -1
- package/skill/tender-accounts/references/feature-flags.md +90 -0
- package/skill/tender-accounts/references/managed-source.md +45 -2
- package/skill/tender-accounts/references/skills.md +7 -0
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);
|
|
@@ -1834,7 +1890,7 @@ async function linkProject(cwd, options, runtime) {
|
|
|
1834
1890
|
}
|
|
1835
1891
|
async function doctor(cwd, options, runtime) {
|
|
1836
1892
|
const checks = [];
|
|
1837
|
-
checks.push(await installedSkillCheck(runtime.homeDirectory));
|
|
1893
|
+
checks.push(await installedSkillCheck(runtime.homeDirectory, { fetcher: runtime.fetcher, cliVersion: CLI_VERSION }));
|
|
1838
1894
|
const nodeMajor = Number(process.versions.node.split(".")[0]);
|
|
1839
1895
|
checks.push({
|
|
1840
1896
|
name: "node",
|
|
@@ -2380,7 +2436,7 @@ async function installSkill(cwd, options, runtime) {
|
|
|
2380
2436
|
if (!base) {
|
|
2381
2437
|
throw new CliError("skill_target_invalid", "--agent must be codex or claude unless --directory is provided.");
|
|
2382
2438
|
}
|
|
2383
|
-
return { status: "installed", agent, ...await installDocumentation(base, options.booleans.has("force"), CLI_VERSION) };
|
|
2439
|
+
return { status: "installed", agent, ...await installDocumentation(base, options.booleans.has("force"), CLI_VERSION, { fetcher: runtime.fetcher, bundled: options.booleans.has("bundled") }) };
|
|
2384
2440
|
}
|
|
2385
2441
|
async function readSkill() {
|
|
2386
2442
|
return await readFile(skillPath(), "utf8");
|
|
@@ -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", "bundled",
|
|
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 = {
|
|
@@ -2521,7 +2670,7 @@ function assertAllowedOptions(topic, options) {
|
|
|
2521
2670
|
init: [...common, "name", "slug", "directory", "template", "dry-run"],
|
|
2522
2671
|
"config example": common,
|
|
2523
2672
|
"skill print": common,
|
|
2524
|
-
"skill install": [...common, "agent", "directory", "force"],
|
|
2673
|
+
"skill install": [...common, "agent", "directory", "force", "bundled"],
|
|
2525
2674
|
"skills list": [...common, ...authentication],
|
|
2526
2675
|
"skills get": [...common, ...authentication],
|
|
2527
2676
|
"skills file": [...common, ...authentication, "path"],
|
|
@@ -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.",
|
|
@@ -2831,7 +2991,7 @@ Account workflow entitlement is required for new starts. Run status is authorita
|
|
|
2831
2991
|
"delivery retry": "Usage: andeo delivery retry --delivery dly_... [--reauthorize] [--json]\n\nRetry one requested or failed developer preview idempotently. --reauthorize explicitly authorizes a legacy preview for the same owner and linked project using the current CLI login; it cannot replace another login's recorded grant or publish production. Normal credential refresh needs no flag. A dwf_ production workflow is resumed by a merchant administrator from Activity.",
|
|
2832
2992
|
skill: "Usage: andeo skill <print|install> [options]",
|
|
2833
2993
|
"skill print": "Usage: andeo skill print [--json]",
|
|
2834
|
-
"skill install": "Usage: andeo skill install [--agent codex|claude] [--directory PATH] [--force] [--json]\n\
|
|
2994
|
+
"skill install": "Usage: andeo skill install [--agent codex|claude] [--directory PATH] [--force] [--bundled] [--json]\n\nFetch the latest compatible public Andeo guide without login. A fresh install falls back to the bundled guide during an outage; existing installations are preserved. --bundled explicitly selects this CLI's offline guide. --force replaces owned files, preserving unrelated files. Installs globally: ~/.agents/skills for Codex or ~/.claude/skills for Claude. --directory overrides the location.\n\nExamples:\n andeo skill install --agent codex --json\n andeo skill install --agent codex --force --json\n andeo skill install --bundled --directory ./skills --json",
|
|
2835
2995
|
skills: "Usage: andeo skills <list|get|file> [options]\n\nAuthenticated, session-only access to Andeo practice skills. Content is not installed into the repository.\n\nExamples:\n andeo skills list --json\n andeo skills get recharge-client\n andeo skills file recharge-customer-operations --path references/subscriptions.md",
|
|
2836
2996
|
"skills list": "Usage: andeo skills list [--profile NAME] [--api-url URL] [--json]\n\nList the practice skills available to the authenticated Andeo user.",
|
|
2837
2997
|
"skills get": "Usage: andeo skills get SKILL_ID [--profile NAME] [--api-url URL] [--json]\n\nRead one skill entrypoint for the current invocation.\n\nExample:\n andeo skills get guardrailed-design-system",
|