@withandeo/cli 0.19.10 → 0.19.12
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 +61 -1
- package/dist/api.d.ts +0 -1
- package/dist/api.d.ts.map +1 -1
- package/dist/api.js +0 -2
- package/dist/api.js.map +1 -1
- package/dist/cli.js +4 -1
- package/dist/cli.js.map +1 -1
- package/dist/contracts.d.ts +8 -0
- package/dist/contracts.d.ts.map +1 -1
- package/dist/documentation.d.ts +23 -0
- package/dist/documentation.d.ts.map +1 -1
- package/dist/documentation.js +157 -13
- package/dist/documentation.js.map +1 -1
- package/dist/flagship-browser.d.ts +18 -0
- package/dist/flagship-browser.d.ts.map +1 -0
- package/dist/flagship-browser.js +63 -0
- package/dist/flagship-browser.js.map +1 -0
- package/dist/flagship-contract.d.ts +4 -37
- package/dist/flagship-contract.d.ts.map +1 -1
- package/dist/flagship-contract.js +23 -65
- package/dist/flagship-contract.js.map +1 -1
- package/dist/flagship-definition.d.ts +67 -0
- package/dist/flagship-definition.d.ts.map +1 -0
- package/dist/flagship-definition.js +207 -0
- package/dist/flagship-definition.js.map +1 -0
- package/dist/flagship-runtime.d.ts +57 -0
- package/dist/flagship-runtime.d.ts.map +1 -0
- package/dist/flagship-runtime.js +79 -0
- package/dist/flagship-runtime.js.map +1 -0
- package/dist/main.d.ts +3 -0
- package/dist/main.d.ts.map +1 -1
- package/dist/main.js +126 -43
- package/dist/main.js.map +1 -1
- package/dist/update-storage.d.ts +3 -0
- package/dist/update-storage.d.ts.map +1 -0
- package/dist/update-storage.js +69 -0
- package/dist/update-storage.js.map +1 -0
- package/dist/updates.d.ts +52 -0
- package/dist/updates.d.ts.map +1 -0
- package/dist/updates.js +297 -0
- package/dist/updates.js.map +1 -0
- package/package.json +9 -2
- package/skill/publication.json +1 -1
- package/skill/tender-accounts/SKILL.md +10 -2
- package/skill/tender-accounts/references/feature-flags.md +140 -69
- package/skill/tender-accounts/references/skills.md +5 -2
|
@@ -0,0 +1,57 @@
|
|
|
1
|
+
import { type FlagContext, type FlagValue, type FlagValueType } from "./flagship-contract.js";
|
|
2
|
+
export interface NativeFlagDetails<T = FlagValue> {
|
|
3
|
+
flagKey: string;
|
|
4
|
+
value: T;
|
|
5
|
+
variant?: string;
|
|
6
|
+
reason?: string;
|
|
7
|
+
errorCode?: string;
|
|
8
|
+
}
|
|
9
|
+
/** Structural native binding contract; no rules, hashes or provider credentials. */
|
|
10
|
+
export interface NativeFlags {
|
|
11
|
+
getBooleanDetails(key: string, fallback: boolean, context?: FlagContext): Promise<NativeFlagDetails<boolean>>;
|
|
12
|
+
getStringDetails(key: string, fallback: string, context?: FlagContext): Promise<NativeFlagDetails<string>>;
|
|
13
|
+
getNumberDetails(key: string, fallback: number, context?: FlagContext): Promise<NativeFlagDetails<number>>;
|
|
14
|
+
getObjectDetails<T>(key: string, fallback: T, context?: FlagContext): Promise<NativeFlagDetails<T>>;
|
|
15
|
+
}
|
|
16
|
+
export interface ClientVisibleFlag {
|
|
17
|
+
key: string;
|
|
18
|
+
type: FlagValueType;
|
|
19
|
+
fallback: FlagValue;
|
|
20
|
+
}
|
|
21
|
+
export interface FlagBootstrap {
|
|
22
|
+
schema: "andeo.flag-bootstrap/v1";
|
|
23
|
+
environment: string;
|
|
24
|
+
refreshAfterMs: number;
|
|
25
|
+
values: Record<string, NativeFlagDetails>;
|
|
26
|
+
}
|
|
27
|
+
export declare function nativeFlagDetails(binding: NativeFlags, flag: ClientVisibleFlag, context: FlagContext): Promise<NativeFlagDetails>;
|
|
28
|
+
/** Application-owned GET endpoint/SSR bootstrap. The server chooses scope and keys;
|
|
29
|
+
* resolveContext must authenticate/enrich trusted properties and select only
|
|
30
|
+
* designated presentation attributes. No management credential or private rules.
|
|
31
|
+
*/
|
|
32
|
+
export declare function createFlagGateway(input: {
|
|
33
|
+
binding: NativeFlags;
|
|
34
|
+
environment: string;
|
|
35
|
+
flags: readonly ClientVisibleFlag[];
|
|
36
|
+
resolveContext: (request: Request) => FlagContext | Promise<FlagContext>;
|
|
37
|
+
refreshAfterMs?: number;
|
|
38
|
+
}): (request: Request) => Promise<Response>;
|
|
39
|
+
export interface FlagExposure {
|
|
40
|
+
flagKey: string;
|
|
41
|
+
variant: string;
|
|
42
|
+
reason?: string;
|
|
43
|
+
environment: string;
|
|
44
|
+
experimentVersion: string;
|
|
45
|
+
}
|
|
46
|
+
/** Call only when the application actually shows an experience. Caller chooses
|
|
47
|
+
* deduplication lifetime and measurement destination; no raw context is emitted.
|
|
48
|
+
*/
|
|
49
|
+
export declare function flagExposure(details: NativeFlagDetails, environment: string, experimentVersion: string): FlagExposure | null;
|
|
50
|
+
/** Optional server-owned one-year anonymous cookie. Reuse on browser and server
|
|
51
|
+
* actions; login does not change it. Explicit reset returns a new identity.
|
|
52
|
+
*/
|
|
53
|
+
export declare function flagVisitorIdentity(request: Request, reset?: boolean): {
|
|
54
|
+
targetingKey: string;
|
|
55
|
+
setCookie?: string;
|
|
56
|
+
};
|
|
57
|
+
//# sourceMappingURL=flagship-runtime.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"flagship-runtime.d.ts","sourceRoot":"","sources":["../src/flagship-runtime.ts"],"names":[],"mappings":"AAAA,OAAO,EAAqD,KAAK,WAAW,EAAE,KAAK,SAAS,EAAE,KAAK,aAAa,EAAE,MAAM,wBAAwB,CAAC;AAEjJ,MAAM,WAAW,iBAAiB,CAAC,CAAC,GAAG,SAAS;IAAI,OAAO,EAAE,MAAM,CAAC;IAAC,KAAK,EAAE,CAAC,CAAC;IAAC,OAAO,CAAC,EAAE,MAAM,CAAC;IAAC,MAAM,CAAC,EAAE,MAAM,CAAC;IAAC,SAAS,CAAC,EAAE,MAAM,CAAA;CAAE;AACtI,oFAAoF;AACpF,MAAM,WAAW,WAAW;IAC1B,iBAAiB,CAAC,GAAG,EAAE,MAAM,EAAE,QAAQ,EAAE,OAAO,EAAE,OAAO,CAAC,EAAE,WAAW,GAAG,OAAO,CAAC,iBAAiB,CAAC,OAAO,CAAC,CAAC,CAAC;IAC9G,gBAAgB,CAAC,GAAG,EAAE,MAAM,EAAE,QAAQ,EAAE,MAAM,EAAE,OAAO,CAAC,EAAE,WAAW,GAAG,OAAO,CAAC,iBAAiB,CAAC,MAAM,CAAC,CAAC,CAAC;IAC3G,gBAAgB,CAAC,GAAG,EAAE,MAAM,EAAE,QAAQ,EAAE,MAAM,EAAE,OAAO,CAAC,EAAE,WAAW,GAAG,OAAO,CAAC,iBAAiB,CAAC,MAAM,CAAC,CAAC,CAAC;IAC3G,gBAAgB,CAAC,CAAC,EAAE,GAAG,EAAE,MAAM,EAAE,QAAQ,EAAE,CAAC,EAAE,OAAO,CAAC,EAAE,WAAW,GAAG,OAAO,CAAC,iBAAiB,CAAC,CAAC,CAAC,CAAC,CAAC;CACrG;AACD,MAAM,WAAW,iBAAiB;IAAG,GAAG,EAAE,MAAM,CAAC;IAAC,IAAI,EAAE,aAAa,CAAC;IAAC,QAAQ,EAAE,SAAS,CAAA;CAAE;AAC5F,MAAM,WAAW,aAAa;IAAG,MAAM,EAAE,yBAAyB,CAAC;IAAC,WAAW,EAAE,MAAM,CAAC;IAAC,cAAc,EAAE,MAAM,CAAC;IAAC,MAAM,EAAE,MAAM,CAAC,MAAM,EAAE,iBAAiB,CAAC,CAAA;CAAE;AAC5J,wBAAsB,iBAAiB,CAAC,OAAO,EAAE,WAAW,EAAE,IAAI,EAAE,iBAAiB,EAAE,OAAO,EAAE,WAAW,GAAG,OAAO,CAAC,iBAAiB,CAAC,CASvI;AACD;;;GAGG;AACH,wBAAgB,iBAAiB,CAAC,KAAK,EAAE;IAAE,OAAO,EAAE,WAAW,CAAC;IAAC,WAAW,EAAE,MAAM,CAAC;IAAC,KAAK,EAAE,SAAS,iBAAiB,EAAE,CAAC;IACxH,cAAc,EAAE,CAAC,OAAO,EAAE,OAAO,KAAK,WAAW,GAAG,OAAO,CAAC,WAAW,CAAC,CAAC;IAAC,cAAc,CAAC,EAAE,MAAM,CAAA;CAAE,aAO5E,OAAO,KAAG,OAAO,CAAC,QAAQ,CAAC,CAmBnD;AACD,MAAM,WAAW,YAAY;IAAG,OAAO,EAAE,MAAM,CAAC;IAAC,OAAO,EAAE,MAAM,CAAC;IAAC,MAAM,CAAC,EAAE,MAAM,CAAC;IAAC,WAAW,EAAE,MAAM,CAAC;IAAC,iBAAiB,EAAE,MAAM,CAAA;CAAE;AACnI;;GAEG;AACH,wBAAgB,YAAY,CAAC,OAAO,EAAE,iBAAiB,EAAE,WAAW,EAAE,MAAM,EAAE,iBAAiB,EAAE,MAAM,GAAG,YAAY,GAAG,IAAI,CAG5H;AACD;;GAEG;AACH,wBAAgB,mBAAmB,CAAC,OAAO,EAAE,OAAO,EAAE,KAAK,UAAQ,GAAG;IAAE,YAAY,EAAE,MAAM,CAAC;IAAC,SAAS,CAAC,EAAE,MAAM,CAAA;CAAE,CAOjH"}
|
|
@@ -0,0 +1,79 @@
|
|
|
1
|
+
import { flagKey, flagContext, flagValueType, safeFlagJson } from "./flagship-contract.js";
|
|
2
|
+
export async function nativeFlagDetails(binding, flag, context) {
|
|
3
|
+
safeFlagJson(flag.fallback);
|
|
4
|
+
if (flagValueType(flag.fallback) !== flag.type)
|
|
5
|
+
throw new Error("flagship_fallback_type_invalid");
|
|
6
|
+
switch (flag.type) {
|
|
7
|
+
case "boolean": return binding.getBooleanDetails(flag.key, flag.fallback, context);
|
|
8
|
+
case "string": return binding.getStringDetails(flag.key, flag.fallback, context);
|
|
9
|
+
case "number": return binding.getNumberDetails(flag.key, flag.fallback, context);
|
|
10
|
+
case "json": return binding.getObjectDetails(flag.key, flag.fallback, context);
|
|
11
|
+
}
|
|
12
|
+
}
|
|
13
|
+
/** Application-owned GET endpoint/SSR bootstrap. The server chooses scope and keys;
|
|
14
|
+
* resolveContext must authenticate/enrich trusted properties and select only
|
|
15
|
+
* designated presentation attributes. No management credential or private rules.
|
|
16
|
+
*/
|
|
17
|
+
export function createFlagGateway(input) {
|
|
18
|
+
if (input.flags.length > 64 || new Set(input.flags.map(f => f.key)).size !== input.flags.length)
|
|
19
|
+
throw new Error("flagship_browser_allowlist_invalid");
|
|
20
|
+
safeFlagJson(input.flags);
|
|
21
|
+
for (const flag of input.flags) {
|
|
22
|
+
flagKey(flag.key);
|
|
23
|
+
if (flagValueType(flag.fallback) !== flag.type)
|
|
24
|
+
throw new Error("flagship_browser_allowlist_invalid");
|
|
25
|
+
}
|
|
26
|
+
const flags = structuredClone(input.flags), { binding, environment, resolveContext } = input;
|
|
27
|
+
const refreshAfterMs = input.refreshAfterMs ?? 30000;
|
|
28
|
+
if (!Number.isSafeInteger(refreshAfterMs) || refreshAfterMs < 0 || refreshAfterMs > 60000)
|
|
29
|
+
throw new Error("flagship_refresh_invalid");
|
|
30
|
+
return async (request) => {
|
|
31
|
+
if (request.method !== "GET")
|
|
32
|
+
return new Response(null, { status: 405, headers: { allow: "GET" } });
|
|
33
|
+
// Queries never select flags, apps, environments or trusted targeting properties.
|
|
34
|
+
if (new URL(request.url).search)
|
|
35
|
+
return new Response(null, { status: 400 });
|
|
36
|
+
const context = flagContext(await resolveContext(request));
|
|
37
|
+
const entries = await Promise.all(flags.map(async (flag) => {
|
|
38
|
+
let details;
|
|
39
|
+
try {
|
|
40
|
+
details = await nativeFlagDetails(binding, flag, context);
|
|
41
|
+
}
|
|
42
|
+
catch {
|
|
43
|
+
details = { flagKey: flag.key, value: flag.fallback, reason: "ERROR", errorCode: "GENERAL" };
|
|
44
|
+
}
|
|
45
|
+
// Explicitly select the public details; never spread provider metadata.
|
|
46
|
+
const publicDetails = { flagKey: flag.key, value: details.value, ...(details.variant ? { variant: details.variant } : {}),
|
|
47
|
+
...(details.reason ? { reason: details.reason } : {}), ...(details.errorCode ? { errorCode: details.errorCode } : {}) };
|
|
48
|
+
safeFlagJson(publicDetails);
|
|
49
|
+
return [flag.key, publicDetails];
|
|
50
|
+
}));
|
|
51
|
+
const bootstrap = { schema: "andeo.flag-bootstrap/v1", environment, refreshAfterMs, values: Object.fromEntries(entries) };
|
|
52
|
+
const body = JSON.stringify(bootstrap);
|
|
53
|
+
if (new TextEncoder().encode(body).length > 64 * 1024)
|
|
54
|
+
throw new Error("flagship_bootstrap_size_exceeded");
|
|
55
|
+
return new Response(body, { headers: { "content-type": "application/json", "cache-control": "private, no-store", vary: "Cookie, Authorization" } });
|
|
56
|
+
};
|
|
57
|
+
}
|
|
58
|
+
/** Call only when the application actually shows an experience. Caller chooses
|
|
59
|
+
* deduplication lifetime and measurement destination; no raw context is emitted.
|
|
60
|
+
*/
|
|
61
|
+
export function flagExposure(details, environment, experimentVersion) {
|
|
62
|
+
if (details.errorCode || details.reason === "ERROR" || details.reason === "DISABLED" || details.reason === "DEFAULT" || !details.variant || !experimentVersion)
|
|
63
|
+
return null;
|
|
64
|
+
return { flagKey: details.flagKey, variant: details.variant, reason: details.reason, environment, experimentVersion };
|
|
65
|
+
}
|
|
66
|
+
/** Optional server-owned one-year anonymous cookie. Reuse on browser and server
|
|
67
|
+
* actions; login does not change it. Explicit reset returns a new identity.
|
|
68
|
+
*/
|
|
69
|
+
export function flagVisitorIdentity(request, reset = false) {
|
|
70
|
+
const existing = request.headers.get("cookie")?.split(";").map(s => s.trim()).find(s => s.startsWith("andeo_flag_visitor="))?.slice(19);
|
|
71
|
+
if (!reset && existing && /^[a-f0-9-]{36}$/.test(existing))
|
|
72
|
+
return { targetingKey: existing };
|
|
73
|
+
const url = new URL(request.url), loopback = ["localhost", "127.0.0.1", "[::1]"].includes(url.hostname);
|
|
74
|
+
if (url.protocol !== "https:" && !loopback)
|
|
75
|
+
throw new Error("flagship_identity_https_required");
|
|
76
|
+
const targetingKey = crypto.randomUUID();
|
|
77
|
+
return { targetingKey, setCookie: `andeo_flag_visitor=${targetingKey}; Path=/; Max-Age=31536000; HttpOnly; SameSite=Lax${url.protocol === "https:" ? "; Secure" : ""}` };
|
|
78
|
+
}
|
|
79
|
+
//# sourceMappingURL=flagship-runtime.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"flagship-runtime.js","sourceRoot":"","sources":["../src/flagship-runtime.ts"],"names":[],"mappings":"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"}
|
package/dist/main.d.ts
CHANGED
|
@@ -2,6 +2,7 @@ import { type CliAuthStore } from "./auth-store.js";
|
|
|
2
2
|
import { waitForLoopbackAuthorization } from "./oauth.js";
|
|
3
3
|
import { captureCommand, runProjectCommand } from "./processes.js";
|
|
4
4
|
import { type CliProfileBindings } from "./profile-bindings.js";
|
|
5
|
+
import { type UpdateRunner } from "./updates.js";
|
|
5
6
|
export declare const CLI_VERSION: string;
|
|
6
7
|
export interface CliRuntime {
|
|
7
8
|
defaultApiUrl: string;
|
|
@@ -15,6 +16,8 @@ export interface CliRuntime {
|
|
|
15
16
|
sleep(milliseconds: number): Promise<void>;
|
|
16
17
|
runProjectCommand: typeof runProjectCommand;
|
|
17
18
|
captureCommand: typeof captureCommand;
|
|
19
|
+
updateCommand: UpdateRunner;
|
|
20
|
+
packageDirectory: string;
|
|
18
21
|
authStore: CliAuthStore;
|
|
19
22
|
profileBindings: CliProfileBindings;
|
|
20
23
|
openBrowser(url: string): Promise<boolean>;
|
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;AAI/B,OAAO,EAA6E,KAAK,YAAY,EAAsB,MAAM,cAAc,CAAC;AAQhJ,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,aAAa,EAAE,YAAY,CAAC;IAC5B,gBAAgB,EAAE,MAAM,CAAC;IACzB,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;AA+BD,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
|
@@ -18,9 +18,10 @@ import { captureCommand, runProjectCommand } from "./processes.js";
|
|
|
18
18
|
import { createDefaultProfileBindings, } from "./profile-bindings.js";
|
|
19
19
|
import { initializeStarter } from "./starter.js";
|
|
20
20
|
import { normalizeAndeoEnvironment } from "./andeo-environment.js";
|
|
21
|
-
import { DOC_TOPICS, readDocumentation, installDocumentation, installedSkillCheck } from "./documentation.js";
|
|
21
|
+
import { DOC_TOPICS, readDocumentation, installDocumentation, installedSkillCheck, skillDocumentationStatus, syncDocumentation } from "./documentation.js";
|
|
22
|
+
import { CLI_PACKAGE_DIRECTORY, configureAutoUpgrade, runUpdateCommand, upgradeCli } from "./updates.js";
|
|
22
23
|
import { capabilityPreflight } from "./capability-preflight.js";
|
|
23
|
-
import { flagEnvironment, flagKey,
|
|
24
|
+
import { flagEnvironment, flagKey, validateFlagDefinition, flagContext, FLAGSHIP_SCHEMA, FLAG_DELETION_GUIDANCE, FlagCompatibilityError } from "./flagship-contract.js";
|
|
24
25
|
const packageMetadata = createRequire(import.meta.url)("../package.json");
|
|
25
26
|
if (typeof packageMetadata.version !== "string" || packageMetadata.version.length === 0) {
|
|
26
27
|
throw new Error("Andeo CLI package version is missing.");
|
|
@@ -38,6 +39,8 @@ const DEFAULT_RUNTIME = {
|
|
|
38
39
|
sleep: async (milliseconds) => await new Promise((resolvePromise) => setTimeout(resolvePromise, milliseconds)),
|
|
39
40
|
runProjectCommand,
|
|
40
41
|
captureCommand,
|
|
42
|
+
updateCommand: runUpdateCommand,
|
|
43
|
+
packageDirectory: CLI_PACKAGE_DIRECTORY,
|
|
41
44
|
authStore: createDefaultAuthStore(process.env),
|
|
42
45
|
profileBindings: createDefaultProfileBindings(process.env),
|
|
43
46
|
openBrowser: openSystemBrowser,
|
|
@@ -98,6 +101,16 @@ export async function runCli(argv, overrides = {}) {
|
|
|
98
101
|
}
|
|
99
102
|
}
|
|
100
103
|
async function execute(topic, options, cwd, runtime) {
|
|
104
|
+
if (topic === "artifact finalize") {
|
|
105
|
+
const directory = resolve(cwd, requiredFlag(options, "artifact-directory"));
|
|
106
|
+
const before = await verifyArtifact(directory);
|
|
107
|
+
await packageAppWorkflows(cwd, before.directory, runtime.environment.WRANGLER_ENV);
|
|
108
|
+
const after = await verifyArtifact(directory);
|
|
109
|
+
if (before.envelope.sourceRevision !== after.envelope.sourceRevision)
|
|
110
|
+
throw new CliError("artifact_source_changed", "Packaging changed the attested source revision.");
|
|
111
|
+
return { value: { status: "verified", sourceRevision: after.envelope.sourceRevision, bundleDigest: after.envelope.bundleDigest,
|
|
112
|
+
artifactDirectory: after.directory, workflowCount: after.envelope.manifest.workflows?.declarations.length ?? 0, flags: after.envelope.manifest.flags ?? null } };
|
|
113
|
+
}
|
|
101
114
|
if (topic.startsWith("workflows "))
|
|
102
115
|
return { value: await workflowCommand(topic.slice(10), cwd, options, runtime) };
|
|
103
116
|
if (topic.startsWith("email "))
|
|
@@ -105,6 +118,16 @@ async function execute(topic, options, cwd, runtime) {
|
|
|
105
118
|
if (topic.startsWith("flagship "))
|
|
106
119
|
return { value: await flagshipCommand(topic.slice(9), cwd, options, runtime) };
|
|
107
120
|
switch (topic) {
|
|
121
|
+
case "upgrade":
|
|
122
|
+
if (options.positionals.length)
|
|
123
|
+
throw new CliError("argument_invalid", "upgrade does not accept positional arguments.");
|
|
124
|
+
return { value: await upgradeCli(updateRuntime(runtime)) };
|
|
125
|
+
case "config autoupgrade on":
|
|
126
|
+
case "config autoupgrade off":
|
|
127
|
+
case "config autoupgrade status":
|
|
128
|
+
if (options.positionals.length)
|
|
129
|
+
throw new CliError("argument_invalid", "autoupgrade does not accept positional arguments.");
|
|
130
|
+
return { value: await configureAutoUpgrade(topic.split(" ")[2], updateRuntime(runtime)) };
|
|
108
131
|
case "docs": {
|
|
109
132
|
const name = options.positionals[0];
|
|
110
133
|
if (!name)
|
|
@@ -134,6 +157,14 @@ async function execute(topic, options, cwd, runtime) {
|
|
|
134
157
|
: { raw: await readSkill() };
|
|
135
158
|
case "skill install":
|
|
136
159
|
return { value: await installSkill(cwd, options, runtime) };
|
|
160
|
+
case "skill status":
|
|
161
|
+
case "skill sync": {
|
|
162
|
+
if (options.positionals.length)
|
|
163
|
+
throw new CliError("argument_invalid", "skill status/sync do not accept positional arguments.");
|
|
164
|
+
const { agent, base } = skillDestination(cwd, options, runtime);
|
|
165
|
+
const action = topic === "skill sync" ? syncDocumentation : skillDocumentationStatus;
|
|
166
|
+
return { value: { agent, ...await action(base, CLI_VERSION, { fetcher: runtime.fetcher }) } };
|
|
167
|
+
}
|
|
137
168
|
case "skills list": {
|
|
138
169
|
if (options.positionals.length)
|
|
139
170
|
throw new CliError("skills_arguments_invalid", "skills list does not accept positional arguments.");
|
|
@@ -2424,6 +2455,14 @@ async function optionalContext(cwd) {
|
|
|
2424
2455
|
}
|
|
2425
2456
|
}
|
|
2426
2457
|
async function installSkill(cwd, options, runtime) {
|
|
2458
|
+
const { agent, base } = skillDestination(cwd, options, runtime);
|
|
2459
|
+
return { status: "installed", agent, ...await installDocumentation(base, options.booleans.has("force"), CLI_VERSION, { fetcher: runtime.fetcher, bundled: options.booleans.has("bundled") }) };
|
|
2460
|
+
}
|
|
2461
|
+
function updateRuntime(runtime) {
|
|
2462
|
+
return { ...runtime, version: CLI_VERSION, runner: runtime.updateCommand, now: Date.now,
|
|
2463
|
+
notify: message => { runtime.stderr.write(`${message}\n`); } };
|
|
2464
|
+
}
|
|
2465
|
+
function skillDestination(cwd, options, runtime) {
|
|
2427
2466
|
const agent = options.flags.get("agent") ?? "codex";
|
|
2428
2467
|
const requestedDirectory = options.flags.get("directory");
|
|
2429
2468
|
const base = requestedDirectory
|
|
@@ -2436,7 +2475,7 @@ async function installSkill(cwd, options, runtime) {
|
|
|
2436
2475
|
if (!base) {
|
|
2437
2476
|
throw new CliError("skill_target_invalid", "--agent must be codex or claude unless --directory is provided.");
|
|
2438
2477
|
}
|
|
2439
|
-
return {
|
|
2478
|
+
return { agent, base };
|
|
2440
2479
|
}
|
|
2441
2480
|
async function readSkill() {
|
|
2442
2481
|
return await readFile(skillPath(), "utf8");
|
|
@@ -2467,6 +2506,10 @@ function exampleConfig() {
|
|
|
2467
2506
|
};
|
|
2468
2507
|
}
|
|
2469
2508
|
async function flagshipCommand(command, cwd, options, runtime) {
|
|
2509
|
+
const definitionFile = async () => validateFlagDefinition(JSON.parse(new TextDecoder().decode(await readBoundedFile(resolve(cwd, requiredFlag(options, "file")), 192 * 1024))));
|
|
2510
|
+
if (command === "flags validate")
|
|
2511
|
+
return { schema: FLAGSHIP_SCHEMA, definition: await definitionFile() };
|
|
2512
|
+
const createDefinition = command === "flags create" ? await definitionFile() : undefined;
|
|
2470
2513
|
let environment;
|
|
2471
2514
|
try {
|
|
2472
2515
|
environment = flagEnvironment(requiredFlag(options, "env"));
|
|
@@ -2479,21 +2522,33 @@ async function flagshipCommand(command, cwd, options, runtime) {
|
|
|
2479
2522
|
if (!projectId)
|
|
2480
2523
|
throw new CliError("project_required", "Link a project or pass --project.");
|
|
2481
2524
|
const { client } = await authenticatedClient(cwd, context?.link ?? null, options, runtime);
|
|
2482
|
-
if (command
|
|
2483
|
-
return client.flagship(projectId, ["apps", ...(command === "apps ensure" ? ["ensure"] : [])], environment, command === "apps ensure" ? { body: {} } : {});
|
|
2525
|
+
if (command === "apps ensure" || command === "apps list")
|
|
2526
|
+
return client.flagship(projectId, ["apps", ...(command === "apps ensure" ? ["ensure"] : [])], environment, command === "apps ensure" ? { body: { ...(options.flags.has("collection") ? { collection: options.flags.get("collection") } : {}) } } : {});
|
|
2527
|
+
if (command.startsWith("apps ")) {
|
|
2528
|
+
const action = command.slice(5), segments = ["apps", options.positionals[0], action];
|
|
2529
|
+
if (action === "get")
|
|
2530
|
+
return client.flagship(projectId, segments, environment);
|
|
2531
|
+
const revision = requiredFlag(options, "expected-revision");
|
|
2532
|
+
if (!/^\d+$/.test(revision) || !Number.isSafeInteger(Number(revision)))
|
|
2533
|
+
throw new CliError("flagship_revision_invalid", "Use an observed collection revision.");
|
|
2534
|
+
return client.flagship(projectId, segments, environment, { body: { expectedRevision: Number(revision), ...(action === "rename" ? { name: requiredFlag(options, "name") } : {}), ...(action === "delete" || action === "disconnect" ? { confirm: requiredFlag(options, "confirm") } : {}) } });
|
|
2535
|
+
}
|
|
2484
2536
|
const action = command.slice(6), app = options.positionals[0], key = options.positionals[1];
|
|
2485
2537
|
const segments = ["flags", app, ...(key ? [flagKey(key)] : [])];
|
|
2486
2538
|
if (action === "list" || action === "get")
|
|
2487
2539
|
return client.flagship(projectId, segments, environment);
|
|
2488
2540
|
segments.push(action);
|
|
2489
|
-
if (action === "changelog")
|
|
2541
|
+
if (action === "changelog" || action === "export")
|
|
2490
2542
|
return client.flagship(projectId, segments, environment);
|
|
2543
|
+
if (action === "diff")
|
|
2544
|
+
return client.flagship(projectId, segments, environment, { body: { definition: await definitionFile() } });
|
|
2491
2545
|
if (action === "evaluate") {
|
|
2492
|
-
const
|
|
2493
|
-
|
|
2494
|
-
|
|
2495
|
-
|
|
2496
|
-
|
|
2546
|
+
const file = options.flags.get("context-file");
|
|
2547
|
+
const context = file ? flagContext(JSON.parse(new TextDecoder().decode(await readBoundedFile(resolve(cwd, file), 16 * 1024)))) : {};
|
|
2548
|
+
const type = options.flags.get("type"), fallbackFile = options.flags.get("fallback-file");
|
|
2549
|
+
if (!!type !== !!fallbackFile)
|
|
2550
|
+
throw new CliError("flagship_fallback_type_invalid", "Choose both --type and --fallback-file.");
|
|
2551
|
+
return client.flagship(projectId, segments, environment, { body: { context, ...(type ? { type, fallback: JSON.parse(new TextDecoder().decode(await readBoundedFile(resolve(cwd, fallbackFile), 10000))) } : {}) } });
|
|
2497
2552
|
}
|
|
2498
2553
|
const revision = options.flags.get("expected-revision");
|
|
2499
2554
|
// Read before mutation by default. Server rechecks this revision inside serialization.
|
|
@@ -2508,31 +2563,23 @@ async function flagshipCommand(command, cwd, options, runtime) {
|
|
|
2508
2563
|
else
|
|
2509
2564
|
expectedRevision = (await client.flagship(projectId, segments.slice(0, -1), environment)).revision;
|
|
2510
2565
|
const body = { expectedRevision };
|
|
2511
|
-
if (action === "
|
|
2512
|
-
|
|
2513
|
-
if (
|
|
2514
|
-
throw new CliError("
|
|
2515
|
-
if (
|
|
2516
|
-
body.
|
|
2517
|
-
|
|
2518
|
-
|
|
2519
|
-
|
|
2520
|
-
|
|
2521
|
-
|
|
2522
|
-
|
|
2523
|
-
|
|
2524
|
-
}
|
|
2525
|
-
body.customerIds = customerIds(value);
|
|
2566
|
+
if (action === "apply" || action === "delete") {
|
|
2567
|
+
// General editing requires a reviewed revision and digest, rather than auto-adopting a new baseline.
|
|
2568
|
+
if (revision === undefined)
|
|
2569
|
+
throw new CliError("flagship_revision_required", "Review flags diff, then supply --expected-revision and --expected-digest.");
|
|
2570
|
+
if (action === "apply")
|
|
2571
|
+
body.definition = await definitionFile();
|
|
2572
|
+
body.expectedDigest = requiredFlag(options, "expected-digest");
|
|
2573
|
+
if (action === "delete") {
|
|
2574
|
+
if (options.flags.get("confirm") !== key)
|
|
2575
|
+
throw new CliError("flagship_delete_confirmation_required", `Use --confirm ${key} after cleanup. ${FLAG_DELETION_GUIDANCE}`);
|
|
2576
|
+
body.confirm = key;
|
|
2577
|
+
if (!options.booleans.has("json"))
|
|
2578
|
+
runtime.stderr.write(`${FLAG_DELETION_GUIDANCE}\n`);
|
|
2526
2579
|
}
|
|
2527
2580
|
}
|
|
2528
|
-
if (action === "
|
|
2529
|
-
|
|
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
|
-
}
|
|
2581
|
+
if (action === "create")
|
|
2582
|
+
body.definition = createDefinition;
|
|
2536
2583
|
return client.flagship(projectId, segments, environment, { body });
|
|
2537
2584
|
}
|
|
2538
2585
|
function commandTopic(argv) {
|
|
@@ -2552,10 +2599,16 @@ function commandTopic(argv) {
|
|
|
2552
2599
|
const topic = `preview ${argv[1]}`;
|
|
2553
2600
|
return argv.includes("--help") || argv.includes("-h") ? { topic: "help", values: [topic] } : { topic, values: argv.slice(2) };
|
|
2554
2601
|
}
|
|
2555
|
-
if (parent === "email" || parent === "auth" || parent === "delivery" || parent === "source" || parent === "vars" || parent === "skill" || parent === "skills" || parent === "config" || parent === "production" || parent === "workflows") {
|
|
2602
|
+
if (parent === "artifact" || parent === "email" || parent === "auth" || parent === "delivery" || parent === "source" || parent === "vars" || parent === "skill" || parent === "skills" || parent === "config" || parent === "production" || parent === "workflows") {
|
|
2556
2603
|
const child = argv[1];
|
|
2557
2604
|
if (!child || child.startsWith("--"))
|
|
2558
2605
|
return { topic: "help", values: [parent] };
|
|
2606
|
+
if (parent === "config" && child === "autoupgrade") {
|
|
2607
|
+
const action = argv[2];
|
|
2608
|
+
if (!action || action.startsWith("-") || argv.includes("--help") || argv.includes("-h"))
|
|
2609
|
+
return { topic: "help", values: ["config autoupgrade"] };
|
|
2610
|
+
return { topic: `config autoupgrade ${action}`, values: argv.slice(3) };
|
|
2611
|
+
}
|
|
2559
2612
|
if (parent === "source" && child === "merge" && argv[2] && !argv[2].startsWith("-")) {
|
|
2560
2613
|
const topic = `source merge ${argv[2]}`;
|
|
2561
2614
|
return argv.includes("--help") || argv.includes("-h") ? { topic: "help", values: [topic] } : { topic, values: argv.slice(3) };
|
|
@@ -2627,14 +2680,14 @@ function parseOptions(values) {
|
|
|
2627
2680
|
function assertAllowedOptions(topic, options) {
|
|
2628
2681
|
if (topic.startsWith("flagship ")) {
|
|
2629
2682
|
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
|
|
2683
|
+
const extras = { "apps ensure": ["collection"], "apps rename": ["expected-revision", "name"], "apps delete": ["expected-revision", "confirm"], "apps disconnect": ["expected-revision", "confirm"], "apps reconcile": ["expected-revision"], "flags delete": ["expected-revision", "expected-digest", "confirm"], "flags validate": ["file"], "flags diff": ["file"], "flags apply": ["file", "expected-revision", "expected-digest"], "flags create": ["expected-revision", "file"], "flags enable": ["expected-revision"], "flags disable": ["expected-revision"],
|
|
2684
|
+
"flags evaluate": ["context-file", "type", "fallback-file"], "flags reconcile": ["expected-revision"] };
|
|
2632
2685
|
const sub = topic.slice(9);
|
|
2633
|
-
if (!["apps ensure", "apps list", "flags list", "flags get", "flags
|
|
2686
|
+
if (!["apps ensure", "apps list", "apps get", "apps rename", "apps delete", "apps disconnect", "apps reconcile", "flags list", "flags get", "flags export", "flags delete", "flags validate", "flags diff", "flags apply", "flags create", "flags enable", "flags disable", "flags evaluate", "flags changelog", "flags reconcile"].includes(sub))
|
|
2634
2687
|
throw new CliError("argument_unknown", "Unknown flagship command.");
|
|
2635
2688
|
if ([...options.flags.keys(), ...options.booleans].some(k => ![...common, ...(extras[sub] ?? [])].includes(k)))
|
|
2636
2689
|
throw new CliError("argument_unknown", "Unsupported flagship option.");
|
|
2637
|
-
const length = sub
|
|
2690
|
+
const length = sub === "apps ensure" || sub === "apps list" || sub === "flags validate" ? 0 : sub.startsWith("apps") || sub === "flags list" ? 1 : 2;
|
|
2638
2691
|
if (options.positionals.length !== length)
|
|
2639
2692
|
throw new CliError("argument_required", `Expected ${length} positional arguments for ${topic}.`);
|
|
2640
2693
|
return;
|
|
@@ -2642,6 +2695,11 @@ function assertAllowedOptions(topic, options) {
|
|
|
2642
2695
|
const common = ["cwd", "json"];
|
|
2643
2696
|
const authentication = ["api-url", "token-stdin", "profile"];
|
|
2644
2697
|
const allowed = {
|
|
2698
|
+
"artifact finalize": [...common, "artifact-directory"],
|
|
2699
|
+
upgrade: common,
|
|
2700
|
+
"config autoupgrade on": common,
|
|
2701
|
+
"config autoupgrade off": common,
|
|
2702
|
+
"config autoupgrade status": common,
|
|
2645
2703
|
docs: common,
|
|
2646
2704
|
capabilities: [...common, ...authentication, "project"],
|
|
2647
2705
|
"email capabilities": [...common, ...authentication, "tenant", "project"],
|
|
@@ -2671,6 +2729,8 @@ function assertAllowedOptions(topic, options) {
|
|
|
2671
2729
|
"config example": common,
|
|
2672
2730
|
"skill print": common,
|
|
2673
2731
|
"skill install": [...common, "agent", "directory", "force", "bundled"],
|
|
2732
|
+
"skill status": [...common, "agent", "directory"],
|
|
2733
|
+
"skill sync": [...common, "agent", "directory"],
|
|
2674
2734
|
"skills list": [...common, ...authentication],
|
|
2675
2735
|
"skills get": [...common, ...authentication],
|
|
2676
2736
|
"skills file": [...common, ...authentication, "path"],
|
|
@@ -2880,8 +2940,25 @@ function writeOutput(value, options, runtime) {
|
|
|
2880
2940
|
runtime.stdout.write(`${JSON.stringify(value, null, 2)}\n`);
|
|
2881
2941
|
}
|
|
2882
2942
|
function helpText(topic) {
|
|
2943
|
+
if (topic?.startsWith("config autoupgrade "))
|
|
2944
|
+
topic = "config autoupgrade";
|
|
2883
2945
|
if (topic === "flagship")
|
|
2884
|
-
return
|
|
2946
|
+
return `Usage: andeo flagship apps ensure|list --env ENV [--collection NAME] [--project ID] [--json]
|
|
2947
|
+
andeo flagship apps get|rename|disconnect|delete|reconcile APP_ID --env ENV [--expected-revision N] [--name NAME] [--confirm EXACT_ID]
|
|
2948
|
+
andeo flagship flags validate --file flag.json --json
|
|
2949
|
+
andeo flagship flags list APP_ID --env ENV
|
|
2950
|
+
andeo flagship flags create APP_ID KEY --env ENV --file flag.json [--expected-revision N]
|
|
2951
|
+
andeo flagship flags get|export|enable|disable|changelog|reconcile APP_ID KEY --env ENV [--expected-revision N]
|
|
2952
|
+
andeo flagship flags diff APP_ID KEY --env ENV --file flag.json --json
|
|
2953
|
+
andeo flagship flags apply APP_ID KEY --env ENV --file flag.json --expected-revision N --expected-digest SHA256 --json
|
|
2954
|
+
andeo flagship flags delete APP_ID KEY --env ENV --expected-revision N --expected-digest SHA256 --confirm KEY
|
|
2955
|
+
andeo flagship flags evaluate APP_ID KEY --env ENV [--context-file context.json] [--type boolean|string|number|json --fallback-file fallback.json]
|
|
2956
|
+
|
|
2957
|
+
All definitions use one pinned native Flagship schema. Create requires a complete disabled definition; apply preserves enabled state. Disable returns the configured default, which may differ from the caller fallback. Targeting and rollouts are edited through full definitions; evaluation context uses --context-file. Management numeric/boolean context transport is unverified and fails explicitly; use native evaluation. Pending operations require reconciliation. No conversion analytics or significance calculation is connected.
|
|
2958
|
+
|
|
2959
|
+
Flag deletion requires a disabled saved flag and exact --confirm KEY after cleanup. ${FLAG_DELETION_GUIDANCE} Andeo does not detect remaining evaluations automatically.`;
|
|
2960
|
+
if (topic === "artifact" || topic === "artifact finalize")
|
|
2961
|
+
return "Usage: andeo artifact finalize --artifact-directory DIR [--json]\n\nFinalize workflow coordinators and native Flagship declarations after producer packaging, retaining the attested source revision. Runs offline in the build checkout.";
|
|
2885
2962
|
if (topic === "email" || topic?.startsWith("email "))
|
|
2886
2963
|
return `Usage: andeo email <capabilities|create|list|cursor|wait|read|messages|retire> [options]
|
|
2887
2964
|
|
|
@@ -2911,6 +2988,8 @@ Choose background preview routing with preview-service-set --target-project PROJ
|
|
|
2911
2988
|
Only non-secret JSON belongs in workflow params/events. Keep credentials in the connected service's native secrets.
|
|
2912
2989
|
Account workflow entitlement is required for new starts. Run status is authoritative; list results are a timestamped projection.`;
|
|
2913
2990
|
const topics = {
|
|
2991
|
+
upgrade: "Usage: andeo upgrade [--json]\n\nUpgrade a verified global npm, pnpm or Yarn 1 installation to the latest stable CLI. Explicit upgrades can cross compatibility boundaries. CI, temporary npx and project-local installs must use their package manager. The next invocation uses the new CLI.",
|
|
2992
|
+
"config autoupgrade": "Usage: andeo config autoupgrade <on|off|status> [--json]\n\nGlobal CLI upgrades default on and run after successful commands at most once daily. Before 1.0, only patches within the current minor are automatic; afterward, updates stay in the current major. Off retains daily reminders and independent global skill refresh. Help, version, CI and explicit maintenance commands skip automatic maintenance.",
|
|
2914
2993
|
vars: "Usage: andeo vars <list|set|unset> --environment preview|production [options]\n\nManage saved non-secret runtime variables. Changes apply on the next delivery.",
|
|
2915
2994
|
"source reconnect": "Usage: andeo source reconnect <prepare|status|complete|cancel> [options]\n\nMove a legacy managed repository to its tenant namespace by preparing, pushing, and completing.",
|
|
2916
2995
|
"source reconnect prepare": "Usage: andeo source reconnect prepare --repository SRC_ID --idempotency-key KEY [--project PROJECT_ID] [--json]\n\nPause the old source and prepare its tenant destination. Merchant administrator required.",
|
|
@@ -2989,21 +3068,25 @@ Account workflow entitlement is required for new starts. Run status is authorita
|
|
|
2989
3068
|
"preview entries": "Usage: andeo preview entries [--json]\n\nList eligible published entry apps and the selected default for this linked app.",
|
|
2990
3069
|
"preview open": "Usage: andeo preview open --from dly_...|sbl_... [--through PROJECT_ID] [--return-path /account] [--json]\n\nOpen the exact deployed preview without rebuilding or uploading. Private apps open through their published connections. Opening again renews access.",
|
|
2991
3070
|
"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.",
|
|
2992
|
-
skill: "Usage: andeo skill <print|install> [options]",
|
|
3071
|
+
skill: "Usage: andeo skill <print|install|status|sync> [options]",
|
|
3072
|
+
"skill status": "Usage: andeo skill status [--agent codex|claude] [--directory PATH] [--json]\n\nCheck the installed guide against the compatible public revision. Reports local_changes, unmanaged or not_installed without overwriting files. Offline and incompatible checks fail explicitly.",
|
|
3073
|
+
"skill sync": "Usage: andeo skill sync [--agent codex|claude] [--directory PATH] [--json]\n\nRefresh an existing, unchanged Andeo-managed guide. Preserve local edits and unrelated files. Outages and incompatible bundles retain installed guidance. After status=updated, reread SKILL.md and relevant references in active chats. Use skill install for a missing guide.",
|
|
2993
3074
|
"skill print": "Usage: andeo skill print [--json]",
|
|
2994
3075
|
"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",
|
|
2995
3076
|
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",
|
|
2996
3077
|
"skills list": "Usage: andeo skills list [--profile NAME] [--api-url URL] [--json]\n\nList the practice skills available to the authenticated Andeo user.",
|
|
2997
3078
|
"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",
|
|
2998
3079
|
"skills file": "Usage: andeo skills file SKILL_ID --path PATH [--profile NAME] [--api-url URL] [--json]\n\nRead one catalog-listed supporting file for the current invocation.\n\nExample:\n andeo skills file recharge-customer-operations --path references/subscriptions.md",
|
|
2999
|
-
config: "Usage: andeo config example [--json]",
|
|
3080
|
+
config: "Usage: andeo config example [--json]\n andeo config autoupgrade <on|off|status> [--json]",
|
|
3000
3081
|
"config example": "Usage: andeo config example [--json]\n\nPrint the committed tender-accounts.json contract.",
|
|
3001
3082
|
};
|
|
3002
3083
|
if (topic && topics[topic])
|
|
3003
3084
|
return topics[topic];
|
|
3004
|
-
return `Andeo merchant developer CLI ${CLI_VERSION}\n\nCommands:\n docs Read version-matched offline guides\n capabilities Inspect scoped support, enablement and permissions\n init Create a new Shopify account starter\n auth login Create or refresh the default login\n auth create Create or re-authorize a named login\n auth activate Bind a named login to a directory tree\n auth deactivate Remove an exact directory binding\n auth status Validate one login or scoped credential\n auth list List local login profiles and bindings\n auth delete Revoke one named login\n auth logout Revoke the default or every login\n link Link this checkout to one permitted project\n doctor Check developer readiness\n dev Run the project-owned local dev command\n check Run project validation\n build Package and verify a portable artifact\n upload Retain a build for administrator publication\n preview Build and deploy an exact preview\n preview entries List eligible connected entry apps\n preview open Reopen an exact deployed preview\n source status Inspect Andeo-managed Git and recent builds\n source branches List exact managed branches and default policy\n source compare Compare two managed branches\n source connect Connect this linked project to managed Git\n source reconnect Prepare, inspect, complete or cancel a tenant namespace cutover\n source pull Clone or fast-forward the exact connected repository\n source token Issue a short-lived repository credential\n source push Push the exact commit without persisting a token\n source promote Fast-forward the default after successful preview checks\n source history Run guarded, archived source-history migration\n email Create and reuse merchant email identities\n workflows Inspect and control app-defined background runs\n vars Manage saved per-environment runtime variables\n source policy Inspect or change automatic publication\n tail Stream logs for one exact linked project\n delivery status Read preview workflow state\n delivery retry Retry a failed preview workflow\n skill install Install the Andeo agent skill\n skills list Discover authenticated practice skills\n skills get Read an authenticated practice skill\n skills file Read one authenticated skill resource\n config example Print the project configuration contract\n\nThere is intentionally no production publish command.\nRun andeo <command> --help for examples.`;
|
|
3085
|
+
return `Andeo merchant developer CLI ${CLI_VERSION}\n\nCommands:\n docs Read version-matched offline guides\n capabilities Inspect scoped support, enablement and permissions\n init Create a new Shopify account starter\n auth login Create or refresh the default login\n auth create Create or re-authorize a named login\n auth activate Bind a named login to a directory tree\n auth deactivate Remove an exact directory binding\n auth status Validate one login or scoped credential\n auth list List local login profiles and bindings\n auth delete Revoke one named login\n auth logout Revoke the default or every login\n link Link this checkout to one permitted project\n doctor Check developer readiness\n dev Run the project-owned local dev command\n check Run project validation\n build Package and verify a portable artifact\n upload Retain a build for administrator publication\n preview Build and deploy an exact preview\n preview entries List eligible connected entry apps\n preview open Reopen an exact deployed preview\n source status Inspect Andeo-managed Git and recent builds\n source branches List exact managed branches and default policy\n source compare Compare two managed branches\n source connect Connect this linked project to managed Git\n source reconnect Prepare, inspect, complete or cancel a tenant namespace cutover\n source pull Clone or fast-forward the exact connected repository\n source token Issue a short-lived repository credential\n source push Push the exact commit without persisting a token\n source promote Fast-forward the default after successful preview checks\n source history Run guarded, archived source-history migration\n email Create and reuse merchant email identities\n workflows Inspect and control app-defined background runs\n vars Manage saved per-environment runtime variables\n source policy Inspect or change automatic publication\n tail Stream logs for one exact linked project\n delivery status Read preview workflow state\n delivery retry Retry a failed preview workflow\n upgrade Upgrade a verified global CLI installation\n config autoupgrade Configure automatic CLI upgrades\n skill install Install the Andeo agent skill\n skill status Inspect installed guidance freshness\n skill sync Refresh unchanged managed guidance\n skills list Discover authenticated practice skills\n skills get Read an authenticated practice skill\n skills file Read one authenticated skill resource\n config example Print the project configuration contract\n\nThere is intentionally no production publish command.\nRun andeo <command> --help for examples.`;
|
|
3005
3086
|
}
|
|
3006
3087
|
function normalizeError(error) {
|
|
3088
|
+
if (error instanceof FlagCompatibilityError)
|
|
3089
|
+
return { code: error.code, message: error.message, details: { field: error.field, schema: FLAGSHIP_SCHEMA } };
|
|
3007
3090
|
if (error instanceof CliApiError) {
|
|
3008
3091
|
return { code: error.code, message: error.message, ...(error.hint ? { hint: error.hint } : {}), ...(error.requestId ? { requestId: error.requestId } : {}), ...(error.details ? { details: error.details } : {}) };
|
|
3009
3092
|
}
|