@ubnt/cawcut 0.1.0
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/config/developer-capabilities.json +36 -0
- package/config/products.json +45 -0
- package/dist/commands/app.d.ts +2 -0
- package/dist/commands/app.js +156 -0
- package/dist/commands/app.js.map +1 -0
- package/dist/commands/auth.d.ts +2 -0
- package/dist/commands/auth.js +62 -0
- package/dist/commands/auth.js.map +1 -0
- package/dist/commands/capabilities.d.ts +2 -0
- package/dist/commands/capabilities.js +83 -0
- package/dist/commands/capabilities.js.map +1 -0
- package/dist/commands/generate.d.ts +2 -0
- package/dist/commands/generate.js +73 -0
- package/dist/commands/generate.js.map +1 -0
- package/dist/commands/set.d.ts +2 -0
- package/dist/commands/set.js +25 -0
- package/dist/commands/set.js.map +1 -0
- package/dist/commands/task.d.ts +2 -0
- package/dist/commands/task.js +48 -0
- package/dist/commands/task.js.map +1 -0
- package/dist/index.d.ts +2 -0
- package/dist/index.js +21 -0
- package/dist/index.js.map +1 -0
- package/dist/lib/app-inputs.d.ts +17 -0
- package/dist/lib/app-inputs.js +191 -0
- package/dist/lib/app-inputs.js.map +1 -0
- package/dist/lib/assets.d.ts +16 -0
- package/dist/lib/assets.js +42 -0
- package/dist/lib/assets.js.map +1 -0
- package/dist/lib/capabilities.d.ts +6 -0
- package/dist/lib/capabilities.js +17 -0
- package/dist/lib/capabilities.js.map +1 -0
- package/dist/lib/capability-catalog.d.ts +56 -0
- package/dist/lib/capability-catalog.js +135 -0
- package/dist/lib/capability-catalog.js.map +1 -0
- package/dist/lib/capability-catalog.json +2743 -0
- package/dist/lib/config.d.ts +46 -0
- package/dist/lib/config.js +177 -0
- package/dist/lib/config.js.map +1 -0
- package/dist/lib/credentials.d.ts +11 -0
- package/dist/lib/credentials.js +47 -0
- package/dist/lib/credentials.js.map +1 -0
- package/dist/lib/developer.d.ts +36 -0
- package/dist/lib/developer.js +75 -0
- package/dist/lib/developer.js.map +1 -0
- package/dist/lib/download.d.ts +3 -0
- package/dist/lib/download.js +38 -0
- package/dist/lib/download.js.map +1 -0
- package/dist/lib/generate-request.d.ts +20 -0
- package/dist/lib/generate-request.js +74 -0
- package/dist/lib/generate-request.js.map +1 -0
- package/dist/lib/http.d.ts +16 -0
- package/dist/lib/http.js +90 -0
- package/dist/lib/http.js.map +1 -0
- package/dist/lib/local-config.d.ts +6 -0
- package/dist/lib/local-config.js +23 -0
- package/dist/lib/local-config.js.map +1 -0
- package/dist/lib/oauth.d.ts +25 -0
- package/dist/lib/oauth.js +141 -0
- package/dist/lib/oauth.js.map +1 -0
- package/dist/lib/output.d.ts +4 -0
- package/dist/lib/output.js +22 -0
- package/dist/lib/output.js.map +1 -0
- package/dist/lib/poll.d.ts +12 -0
- package/dist/lib/poll.js +26 -0
- package/dist/lib/poll.js.map +1 -0
- package/dist/lib/result-output.d.ts +18 -0
- package/dist/lib/result-output.js +82 -0
- package/dist/lib/result-output.js.map +1 -0
- package/dist/lib/spinner.d.ts +5 -0
- package/dist/lib/spinner.js +39 -0
- package/dist/lib/spinner.js.map +1 -0
- package/dist/lib/validate-generate.d.ts +21 -0
- package/dist/lib/validate-generate.js +191 -0
- package/dist/lib/validate-generate.js.map +1 -0
- package/package.json +34 -0
package/dist/lib/http.js
ADDED
|
@@ -0,0 +1,90 @@
|
|
|
1
|
+
import { refreshUrl } from "./config.js";
|
|
2
|
+
import { isAccessTokenExpired, readCredentials, writeCredentials, } from "./credentials.js";
|
|
3
|
+
import { formatApiError } from "./output.js";
|
|
4
|
+
export const TOKEN_EXPIRED_MESSAGE = "Token expired, run `cawcut auth login`";
|
|
5
|
+
export class ApiError extends Error {
|
|
6
|
+
status;
|
|
7
|
+
body;
|
|
8
|
+
constructor(message, status, body) {
|
|
9
|
+
super(message);
|
|
10
|
+
this.status = status;
|
|
11
|
+
this.body = body;
|
|
12
|
+
this.name = "ApiError";
|
|
13
|
+
}
|
|
14
|
+
}
|
|
15
|
+
function apiBase(config, target) {
|
|
16
|
+
return target === "workflow" ? config.workflowApiBase : config.userApiBase;
|
|
17
|
+
}
|
|
18
|
+
function resolveUrl(config, path, target) {
|
|
19
|
+
if (/^https?:\/\//i.test(path)) {
|
|
20
|
+
return path;
|
|
21
|
+
}
|
|
22
|
+
const base = apiBase(config, target);
|
|
23
|
+
return `${base}${path.startsWith("/") ? path : `/${path}`}`;
|
|
24
|
+
}
|
|
25
|
+
async function refreshAccessToken(config, credentials, fetchImpl) {
|
|
26
|
+
const response = await fetchImpl(refreshUrl(config), {
|
|
27
|
+
method: "POST",
|
|
28
|
+
headers: { "Content-Type": "application/json" },
|
|
29
|
+
body: JSON.stringify({
|
|
30
|
+
token: credentials.accessToken,
|
|
31
|
+
refresh_token: credentials.refreshToken,
|
|
32
|
+
user_id: credentials.userId,
|
|
33
|
+
}),
|
|
34
|
+
});
|
|
35
|
+
const body = (await response.json().catch(() => ({})));
|
|
36
|
+
if (!response.ok || !body.data?.token || !body.data.refresh_token || typeof body.data.expire !== "number") {
|
|
37
|
+
throw new ApiError(TOKEN_EXPIRED_MESSAGE, response.status, body);
|
|
38
|
+
}
|
|
39
|
+
const refreshed = {
|
|
40
|
+
accessToken: body.data.token,
|
|
41
|
+
refreshToken: body.data.refresh_token,
|
|
42
|
+
userId: body.data.user_id ?? credentials.userId,
|
|
43
|
+
expire: body.data.expire,
|
|
44
|
+
};
|
|
45
|
+
await writeCredentials(config.credentialsPath, refreshed);
|
|
46
|
+
return refreshed;
|
|
47
|
+
}
|
|
48
|
+
export async function getValidCredentials(config, fetchImpl = fetch) {
|
|
49
|
+
const credentials = await readCredentials(config.credentialsPath);
|
|
50
|
+
if (!credentials) {
|
|
51
|
+
throw new ApiError(TOKEN_EXPIRED_MESSAGE, 401);
|
|
52
|
+
}
|
|
53
|
+
if (!isAccessTokenExpired(credentials)) {
|
|
54
|
+
return credentials;
|
|
55
|
+
}
|
|
56
|
+
return refreshAccessToken(config, credentials, fetchImpl);
|
|
57
|
+
}
|
|
58
|
+
export async function apiRequest(config, path, options = {}, fetchImpl = fetch) {
|
|
59
|
+
const target = options.api ?? "workflow";
|
|
60
|
+
const url = resolveUrl(config, path, target);
|
|
61
|
+
const headers = new Headers(options.headers);
|
|
62
|
+
let credentials;
|
|
63
|
+
if (!options.skipAuth) {
|
|
64
|
+
credentials = await getValidCredentials(config, fetchImpl);
|
|
65
|
+
headers.set("Authorization", `Bearer ${credentials.accessToken}`);
|
|
66
|
+
}
|
|
67
|
+
const response = await fetchImpl(url, {
|
|
68
|
+
...options,
|
|
69
|
+
headers,
|
|
70
|
+
});
|
|
71
|
+
if (response.status !== 401 || options.skipAuth || !credentials) {
|
|
72
|
+
if (!response.ok) {
|
|
73
|
+
const body = await response.clone().json().catch(() => undefined);
|
|
74
|
+
throw new ApiError(formatApiError(response.status, body), response.status, body);
|
|
75
|
+
}
|
|
76
|
+
return response;
|
|
77
|
+
}
|
|
78
|
+
const refreshed = await refreshAccessToken(config, credentials, fetchImpl);
|
|
79
|
+
headers.set("Authorization", `Bearer ${refreshed.accessToken}`);
|
|
80
|
+
const retry = await fetchImpl(url, {
|
|
81
|
+
...options,
|
|
82
|
+
headers,
|
|
83
|
+
});
|
|
84
|
+
if (!retry.ok) {
|
|
85
|
+
const body = await retry.clone().json().catch(() => undefined);
|
|
86
|
+
throw new ApiError(formatApiError(retry.status, body), retry.status, body);
|
|
87
|
+
}
|
|
88
|
+
return retry;
|
|
89
|
+
}
|
|
90
|
+
//# sourceMappingURL=http.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"http.js","sourceRoot":"","sources":["../../src/lib/http.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,6 @@
|
|
|
1
|
+
export interface LocalConfig {
|
|
2
|
+
env?: string;
|
|
3
|
+
}
|
|
4
|
+
export declare function localConfigPath(credentialsDir: string): string;
|
|
5
|
+
export declare function readLocalConfig(path: string): Promise<LocalConfig>;
|
|
6
|
+
export declare function writeLocalConfig(path: string, config: LocalConfig): Promise<void>;
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
import { mkdir, readFile, writeFile } from "node:fs/promises";
|
|
2
|
+
import { homedir } from "node:os";
|
|
3
|
+
import { dirname, join } from "node:path";
|
|
4
|
+
export function localConfigPath(credentialsDir) {
|
|
5
|
+
return join(homedir(), credentialsDir, "config.json");
|
|
6
|
+
}
|
|
7
|
+
export async function readLocalConfig(path) {
|
|
8
|
+
try {
|
|
9
|
+
const raw = await readFile(path, "utf8");
|
|
10
|
+
return JSON.parse(raw);
|
|
11
|
+
}
|
|
12
|
+
catch (error) {
|
|
13
|
+
if (error.code === "ENOENT") {
|
|
14
|
+
return {};
|
|
15
|
+
}
|
|
16
|
+
throw error;
|
|
17
|
+
}
|
|
18
|
+
}
|
|
19
|
+
export async function writeLocalConfig(path, config) {
|
|
20
|
+
await mkdir(dirname(path), { recursive: true });
|
|
21
|
+
await writeFile(path, `${JSON.stringify(config, null, 2)}\n`, "utf8");
|
|
22
|
+
}
|
|
23
|
+
//# sourceMappingURL=local-config.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"local-config.js","sourceRoot":"","sources":["../../src/lib/local-config.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,KAAK,EAAE,QAAQ,EAAE,SAAS,EAAE,MAAM,kBAAkB,CAAC;AAC9D,OAAO,EAAE,OAAO,EAAE,MAAM,SAAS,CAAC;AAClC,OAAO,EAAE,OAAO,EAAE,IAAI,EAAE,MAAM,WAAW,CAAC;AAM1C,MAAM,UAAU,eAAe,CAAC,cAAsB;IACpD,OAAO,IAAI,CAAC,OAAO,EAAE,EAAE,cAAc,EAAE,aAAa,CAAC,CAAC;AACxD,CAAC;AAED,MAAM,CAAC,KAAK,UAAU,eAAe,CAAC,IAAY;IAChD,IAAI,CAAC;QACH,MAAM,GAAG,GAAG,MAAM,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAC,CAAC;QACzC,OAAO,IAAI,CAAC,KAAK,CAAC,GAAG,CAAgB,CAAC;IACxC,CAAC;IAAC,OAAO,KAAK,EAAE,CAAC;QACf,IAAK,KAA+B,CAAC,IAAI,KAAK,QAAQ,EAAE,CAAC;YACvD,OAAO,EAAE,CAAC;QACZ,CAAC;QACD,MAAM,KAAK,CAAC;IACd,CAAC;AACH,CAAC;AAED,MAAM,CAAC,KAAK,UAAU,gBAAgB,CAAC,IAAY,EAAE,MAAmB;IACtE,MAAM,KAAK,CAAC,OAAO,CAAC,IAAI,CAAC,EAAE,EAAE,SAAS,EAAE,IAAI,EAAE,CAAC,CAAC;IAChD,MAAM,SAAS,CAAC,IAAI,EAAE,GAAG,IAAI,CAAC,SAAS,CAAC,MAAM,EAAE,IAAI,EAAE,CAAC,CAAC,IAAI,EAAE,MAAM,CAAC,CAAC;AACxE,CAAC"}
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
import { type ResolvedConfig } from "./config.js";
|
|
2
|
+
import type { Credentials } from "./credentials.js";
|
|
3
|
+
export declare const OAUTH_DENIED_ERROR = "access_denied";
|
|
4
|
+
export declare const OAUTH_PORT_CONFLICT_ERROR = "local_callback_port_conflict";
|
|
5
|
+
export declare const OAUTH_TIMEOUT_ERROR = "oauth_timeout";
|
|
6
|
+
export interface CallbackResult {
|
|
7
|
+
stateToken?: string;
|
|
8
|
+
error?: string;
|
|
9
|
+
}
|
|
10
|
+
export interface OAuthLoginOptions {
|
|
11
|
+
openBrowser?: (url: string) => Promise<void>;
|
|
12
|
+
portRetries?: number;
|
|
13
|
+
callbackTimeoutMs?: number;
|
|
14
|
+
}
|
|
15
|
+
export declare function buildCallbackUrl(host: string, port: number): string;
|
|
16
|
+
export declare function openBrowser(url: string): Promise<void>;
|
|
17
|
+
export declare function waitForOAuthCallback(host?: string, options?: {
|
|
18
|
+
portRetries?: number;
|
|
19
|
+
timeoutMs?: number;
|
|
20
|
+
}): Promise<{
|
|
21
|
+
port: number;
|
|
22
|
+
result: Promise<CallbackResult>;
|
|
23
|
+
}>;
|
|
24
|
+
export declare function exchangeStateToken(config: ResolvedConfig, stateToken: string, fetchImpl?: typeof fetch): Promise<Credentials>;
|
|
25
|
+
export declare function runOAuthLogin(config: ResolvedConfig, options?: OAuthLoginOptions): Promise<Credentials>;
|
|
@@ -0,0 +1,141 @@
|
|
|
1
|
+
import { execFile } from "node:child_process";
|
|
2
|
+
import { createServer } from "node:http";
|
|
3
|
+
import { promisify } from "node:util";
|
|
4
|
+
import { buildConsentUrl, exchangeUrl, } from "./config.js";
|
|
5
|
+
import { formatApiError } from "./output.js";
|
|
6
|
+
const execFileAsync = promisify(execFile);
|
|
7
|
+
export const OAUTH_DENIED_ERROR = "access_denied";
|
|
8
|
+
export const OAUTH_PORT_CONFLICT_ERROR = "local_callback_port_conflict";
|
|
9
|
+
export const OAUTH_TIMEOUT_ERROR = "oauth_timeout";
|
|
10
|
+
const DEFAULT_PORT_RETRIES = 3;
|
|
11
|
+
const CALLBACK_TIMEOUT_MS = 5 * 60 * 1000;
|
|
12
|
+
export function buildCallbackUrl(host, port) {
|
|
13
|
+
return `http://${host}:${port}/callback`;
|
|
14
|
+
}
|
|
15
|
+
export async function openBrowser(url) {
|
|
16
|
+
const platform = process.platform;
|
|
17
|
+
if (platform === "darwin") {
|
|
18
|
+
await execFileAsync("open", [url]);
|
|
19
|
+
return;
|
|
20
|
+
}
|
|
21
|
+
if (platform === "win32") {
|
|
22
|
+
await execFileAsync("cmd", ["/c", "start", "", url]);
|
|
23
|
+
return;
|
|
24
|
+
}
|
|
25
|
+
await execFileAsync("xdg-open", [url]);
|
|
26
|
+
}
|
|
27
|
+
export function waitForOAuthCallback(host = "127.0.0.1", options) {
|
|
28
|
+
const portRetries = options?.portRetries ?? DEFAULT_PORT_RETRIES;
|
|
29
|
+
const timeoutMs = options?.timeoutMs ?? CALLBACK_TIMEOUT_MS;
|
|
30
|
+
let resolveCallback;
|
|
31
|
+
const result = new Promise((resolve) => {
|
|
32
|
+
resolveCallback = resolve;
|
|
33
|
+
});
|
|
34
|
+
return new Promise((resolve, reject) => {
|
|
35
|
+
let attempts = 0;
|
|
36
|
+
let server;
|
|
37
|
+
let timer;
|
|
38
|
+
const cleanup = () => {
|
|
39
|
+
if (timer) {
|
|
40
|
+
clearTimeout(timer);
|
|
41
|
+
}
|
|
42
|
+
server?.close();
|
|
43
|
+
};
|
|
44
|
+
const tryListen = () => {
|
|
45
|
+
attempts += 1;
|
|
46
|
+
server = createServer((req, res) => {
|
|
47
|
+
if (!req.url?.startsWith("/callback")) {
|
|
48
|
+
res.statusCode = 404;
|
|
49
|
+
res.end("Not found");
|
|
50
|
+
return;
|
|
51
|
+
}
|
|
52
|
+
const requestUrl = new URL(req.url, `http://${host}`);
|
|
53
|
+
const error = requestUrl.searchParams.get("error") ?? undefined;
|
|
54
|
+
const stateToken = requestUrl.searchParams.get("state_token") ?? undefined;
|
|
55
|
+
res.statusCode = 200;
|
|
56
|
+
res.setHeader("Content-Type", "text/plain; charset=utf-8");
|
|
57
|
+
res.end("You can close this window and return to the terminal.");
|
|
58
|
+
cleanup();
|
|
59
|
+
if (error) {
|
|
60
|
+
resolveCallback({ error });
|
|
61
|
+
return;
|
|
62
|
+
}
|
|
63
|
+
if (!stateToken) {
|
|
64
|
+
resolveCallback({ error: "missing_state_token" });
|
|
65
|
+
return;
|
|
66
|
+
}
|
|
67
|
+
resolveCallback({ stateToken });
|
|
68
|
+
});
|
|
69
|
+
server.once("error", (err) => {
|
|
70
|
+
if (err.code === "EADDRINUSE" && attempts < portRetries) {
|
|
71
|
+
server?.close();
|
|
72
|
+
tryListen();
|
|
73
|
+
return;
|
|
74
|
+
}
|
|
75
|
+
if (err.code === "EADDRINUSE") {
|
|
76
|
+
reject(new Error(OAUTH_PORT_CONFLICT_ERROR));
|
|
77
|
+
return;
|
|
78
|
+
}
|
|
79
|
+
reject(err);
|
|
80
|
+
});
|
|
81
|
+
server.listen(0, host, () => {
|
|
82
|
+
const address = server?.address();
|
|
83
|
+
if (!address || typeof address === "string") {
|
|
84
|
+
reject(new Error("failed to bind local OAuth callback server"));
|
|
85
|
+
return;
|
|
86
|
+
}
|
|
87
|
+
timer = setTimeout(() => {
|
|
88
|
+
cleanup();
|
|
89
|
+
resolveCallback({ error: OAUTH_TIMEOUT_ERROR });
|
|
90
|
+
}, timeoutMs);
|
|
91
|
+
result.finally(() => cleanup());
|
|
92
|
+
resolve({ port: address.port, result });
|
|
93
|
+
});
|
|
94
|
+
};
|
|
95
|
+
tryListen();
|
|
96
|
+
});
|
|
97
|
+
}
|
|
98
|
+
export async function exchangeStateToken(config, stateToken, fetchImpl = fetch) {
|
|
99
|
+
const response = await fetchImpl(exchangeUrl(config), {
|
|
100
|
+
method: "POST",
|
|
101
|
+
headers: { "Content-Type": "application/json" },
|
|
102
|
+
body: JSON.stringify({ state_token: stateToken }),
|
|
103
|
+
});
|
|
104
|
+
const body = (await response.json().catch(() => ({})));
|
|
105
|
+
if (!response.ok) {
|
|
106
|
+
throw new Error(formatApiError(response.status, body));
|
|
107
|
+
}
|
|
108
|
+
const data = body.data;
|
|
109
|
+
if (!data?.token || !data.refresh_token || typeof data.user_id !== "number" || typeof data.expire !== "number") {
|
|
110
|
+
throw new Error("exchange response missing token fields");
|
|
111
|
+
}
|
|
112
|
+
return {
|
|
113
|
+
accessToken: data.token,
|
|
114
|
+
refreshToken: data.refresh_token,
|
|
115
|
+
userId: data.user_id,
|
|
116
|
+
expire: data.expire,
|
|
117
|
+
};
|
|
118
|
+
}
|
|
119
|
+
export async function runOAuthLogin(config, options) {
|
|
120
|
+
const host = "127.0.0.1";
|
|
121
|
+
const { port, result } = await waitForOAuthCallback(host, {
|
|
122
|
+
portRetries: options?.portRetries,
|
|
123
|
+
timeoutMs: options?.callbackTimeoutMs,
|
|
124
|
+
});
|
|
125
|
+
const redirectUri = buildCallbackUrl(host, port);
|
|
126
|
+
const consentUrl = buildConsentUrl(config, redirectUri);
|
|
127
|
+
const open = options?.openBrowser ?? openBrowser;
|
|
128
|
+
await open(consentUrl);
|
|
129
|
+
const callback = await result;
|
|
130
|
+
if (callback.error === OAUTH_DENIED_ERROR) {
|
|
131
|
+
throw new Error(OAUTH_DENIED_ERROR);
|
|
132
|
+
}
|
|
133
|
+
if (callback.error === OAUTH_TIMEOUT_ERROR) {
|
|
134
|
+
throw new Error("Authorization timed out. Run `cawcut auth login` again.");
|
|
135
|
+
}
|
|
136
|
+
if (callback.error || !callback.stateToken) {
|
|
137
|
+
throw new Error("Authorization failed: missing state token in callback.");
|
|
138
|
+
}
|
|
139
|
+
return exchangeStateToken(config, callback.stateToken);
|
|
140
|
+
}
|
|
141
|
+
//# sourceMappingURL=oauth.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"oauth.js","sourceRoot":"","sources":["../../src/lib/oauth.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
export function success(message) {
|
|
2
|
+
console.log(message);
|
|
3
|
+
}
|
|
4
|
+
export function info(message) {
|
|
5
|
+
console.error(message);
|
|
6
|
+
}
|
|
7
|
+
export function error(message) {
|
|
8
|
+
console.error(message);
|
|
9
|
+
}
|
|
10
|
+
export function formatApiError(status, body) {
|
|
11
|
+
if (body && typeof body === "object") {
|
|
12
|
+
const record = body;
|
|
13
|
+
if (typeof record.message === "string" && record.message.length > 0) {
|
|
14
|
+
return record.message;
|
|
15
|
+
}
|
|
16
|
+
if (typeof record.msg === "string" && record.msg.length > 0) {
|
|
17
|
+
return record.msg;
|
|
18
|
+
}
|
|
19
|
+
}
|
|
20
|
+
return `Request failed with status ${status}`;
|
|
21
|
+
}
|
|
22
|
+
//# sourceMappingURL=output.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"output.js","sourceRoot":"","sources":["../../src/lib/output.ts"],"names":[],"mappings":"AAAA,MAAM,UAAU,OAAO,CAAC,OAAe;IACrC,OAAO,CAAC,GAAG,CAAC,OAAO,CAAC,CAAC;AACvB,CAAC;AAED,MAAM,UAAU,IAAI,CAAC,OAAe;IAClC,OAAO,CAAC,KAAK,CAAC,OAAO,CAAC,CAAC;AACzB,CAAC;AAED,MAAM,UAAU,KAAK,CAAC,OAAe;IACnC,OAAO,CAAC,KAAK,CAAC,OAAO,CAAC,CAAC;AACzB,CAAC;AAED,MAAM,UAAU,cAAc,CAAC,MAAc,EAAE,IAAa;IAC1D,IAAI,IAAI,IAAI,OAAO,IAAI,KAAK,QAAQ,EAAE,CAAC;QACrC,MAAM,MAAM,GAAG,IAA+B,CAAC;QAC/C,IAAI,OAAO,MAAM,CAAC,OAAO,KAAK,QAAQ,IAAI,MAAM,CAAC,OAAO,CAAC,MAAM,GAAG,CAAC,EAAE,CAAC;YACpE,OAAO,MAAM,CAAC,OAAO,CAAC;QACxB,CAAC;QACD,IAAI,OAAO,MAAM,CAAC,GAAG,KAAK,QAAQ,IAAI,MAAM,CAAC,GAAG,CAAC,MAAM,GAAG,CAAC,EAAE,CAAC;YAC5D,OAAO,MAAM,CAAC,GAAG,CAAC;QACpB,CAAC;IACH,CAAC;IACD,OAAO,8BAA8B,MAAM,EAAE,CAAC;AAChD,CAAC"}
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
export declare const TERMINAL_TASK_STATUSES: Set<string>;
|
|
2
|
+
export declare class PollTimeoutError extends Error {
|
|
3
|
+
constructor();
|
|
4
|
+
}
|
|
5
|
+
export interface PollOptions {
|
|
6
|
+
intervalMs?: number;
|
|
7
|
+
maxAttempts?: number;
|
|
8
|
+
onStatus?: (status: string) => void;
|
|
9
|
+
}
|
|
10
|
+
export declare function pollUntilTerminal<T extends {
|
|
11
|
+
status: string;
|
|
12
|
+
}>(fetchStatus: () => Promise<T>, options?: PollOptions): Promise<T>;
|
package/dist/lib/poll.js
ADDED
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
export const TERMINAL_TASK_STATUSES = new Set(["done", "failed"]);
|
|
2
|
+
export class PollTimeoutError extends Error {
|
|
3
|
+
constructor() {
|
|
4
|
+
super("Polling timed out before task reached terminal status");
|
|
5
|
+
this.name = "PollTimeoutError";
|
|
6
|
+
}
|
|
7
|
+
}
|
|
8
|
+
function sleep(ms) {
|
|
9
|
+
return new Promise((resolve) => setTimeout(resolve, ms));
|
|
10
|
+
}
|
|
11
|
+
export async function pollUntilTerminal(fetchStatus, options = {}) {
|
|
12
|
+
const intervalMs = options.intervalMs ?? 3000;
|
|
13
|
+
const maxAttempts = options.maxAttempts ?? 100;
|
|
14
|
+
for (let attempt = 0; attempt < maxAttempts; attempt += 1) {
|
|
15
|
+
const result = await fetchStatus();
|
|
16
|
+
options.onStatus?.(result.status);
|
|
17
|
+
if (TERMINAL_TASK_STATUSES.has(result.status)) {
|
|
18
|
+
return result;
|
|
19
|
+
}
|
|
20
|
+
if (attempt < maxAttempts - 1) {
|
|
21
|
+
await sleep(intervalMs);
|
|
22
|
+
}
|
|
23
|
+
}
|
|
24
|
+
throw new PollTimeoutError();
|
|
25
|
+
}
|
|
26
|
+
//# sourceMappingURL=poll.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"poll.js","sourceRoot":"","sources":["../../src/lib/poll.ts"],"names":[],"mappings":"AAAA,MAAM,CAAC,MAAM,sBAAsB,GAAG,IAAI,GAAG,CAAC,CAAC,MAAM,EAAE,QAAQ,CAAC,CAAC,CAAC;AAElE,MAAM,OAAO,gBAAiB,SAAQ,KAAK;IACzC;QACE,KAAK,CAAC,uDAAuD,CAAC,CAAC;QAC/D,IAAI,CAAC,IAAI,GAAG,kBAAkB,CAAC;IACjC,CAAC;CACF;AAQD,SAAS,KAAK,CAAC,EAAU;IACvB,OAAO,IAAI,OAAO,CAAC,CAAC,OAAO,EAAE,EAAE,CAAC,UAAU,CAAC,OAAO,EAAE,EAAE,CAAC,CAAC,CAAC;AAC3D,CAAC;AAED,MAAM,CAAC,KAAK,UAAU,iBAAiB,CACrC,WAA6B,EAC7B,UAAuB,EAAE;IAEzB,MAAM,UAAU,GAAG,OAAO,CAAC,UAAU,IAAI,IAAI,CAAC;IAC9C,MAAM,WAAW,GAAG,OAAO,CAAC,WAAW,IAAI,GAAG,CAAC;IAE/C,KAAK,IAAI,OAAO,GAAG,CAAC,EAAE,OAAO,GAAG,WAAW,EAAE,OAAO,IAAI,CAAC,EAAE,CAAC;QAC1D,MAAM,MAAM,GAAG,MAAM,WAAW,EAAE,CAAC;QACnC,OAAO,CAAC,QAAQ,EAAE,CAAC,MAAM,CAAC,MAAM,CAAC,CAAC;QAClC,IAAI,sBAAsB,CAAC,GAAG,CAAC,MAAM,CAAC,MAAM,CAAC,EAAE,CAAC;YAC9C,OAAO,MAAM,CAAC;QAChB,CAAC;QACD,IAAI,OAAO,GAAG,WAAW,GAAG,CAAC,EAAE,CAAC;YAC9B,MAAM,KAAK,CAAC,UAAU,CAAC,CAAC;QAC1B,CAAC;IACH,CAAC;IAED,MAAM,IAAI,gBAAgB,EAAE,CAAC;AAC/B,CAAC"}
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
import type { ResolvedConfig } from "./config.js";
|
|
2
|
+
import { type AsyncResult } from "./developer.js";
|
|
3
|
+
import type { Spinner } from "./spinner.js";
|
|
4
|
+
export interface ResultOutputOptions {
|
|
5
|
+
wait?: boolean;
|
|
6
|
+
download?: string | boolean;
|
|
7
|
+
json?: boolean;
|
|
8
|
+
actionLabel?: string;
|
|
9
|
+
spinner?: Spinner;
|
|
10
|
+
}
|
|
11
|
+
export interface ResolvedResult {
|
|
12
|
+
result_url?: string;
|
|
13
|
+
task_id?: string;
|
|
14
|
+
local_path?: string;
|
|
15
|
+
status?: string;
|
|
16
|
+
error?: string;
|
|
17
|
+
}
|
|
18
|
+
export declare function emitAsyncResult(config: ResolvedConfig, result: AsyncResult, options: ResultOutputOptions, fetchImpl?: typeof fetch): Promise<void>;
|
|
@@ -0,0 +1,82 @@
|
|
|
1
|
+
import { developerGetTask, } from "./developer.js";
|
|
2
|
+
import { downloadFromUrl, resolveDownloadDir } from "./download.js";
|
|
3
|
+
import { pollUntilTerminal, PollTimeoutError } from "./poll.js";
|
|
4
|
+
import { error, success } from "./output.js";
|
|
5
|
+
async function resolveResultUrl(config, result, wait, fetchImpl, options) {
|
|
6
|
+
if (result.result_url) {
|
|
7
|
+
return { result_url: result.result_url, task_id: result.task_id };
|
|
8
|
+
}
|
|
9
|
+
const taskId = result.task_id;
|
|
10
|
+
if (!taskId) {
|
|
11
|
+
return {};
|
|
12
|
+
}
|
|
13
|
+
if (!wait) {
|
|
14
|
+
return { task_id: taskId };
|
|
15
|
+
}
|
|
16
|
+
try {
|
|
17
|
+
const status = await pollUntilTerminal(() => developerGetTask(config, taskId, fetchImpl), {
|
|
18
|
+
onStatus: (s) => options?.spinner?.update(`Waiting for task (${s})…`),
|
|
19
|
+
});
|
|
20
|
+
if (status.status === "failed") {
|
|
21
|
+
return {
|
|
22
|
+
task_id: taskId,
|
|
23
|
+
status: status.status,
|
|
24
|
+
error: status.error?.trim() || "Task failed.",
|
|
25
|
+
};
|
|
26
|
+
}
|
|
27
|
+
return {
|
|
28
|
+
task_id: taskId,
|
|
29
|
+
status: status.status,
|
|
30
|
+
result_url: status.result_url,
|
|
31
|
+
};
|
|
32
|
+
}
|
|
33
|
+
catch (err) {
|
|
34
|
+
if (err instanceof PollTimeoutError) {
|
|
35
|
+
return { task_id: taskId, status: "timeout" };
|
|
36
|
+
}
|
|
37
|
+
throw err;
|
|
38
|
+
}
|
|
39
|
+
}
|
|
40
|
+
export async function emitAsyncResult(config, result, options, fetchImpl = fetch) {
|
|
41
|
+
const actionLabel = options.actionLabel ?? "Generation";
|
|
42
|
+
// Print task_id before polling so it is captured even on timeout or process kill.
|
|
43
|
+
if (result.task_id && !options.json) {
|
|
44
|
+
success(`Task: ${result.task_id}`);
|
|
45
|
+
}
|
|
46
|
+
const resolved = await resolveResultUrl(config, result, Boolean(options.wait), fetchImpl, options);
|
|
47
|
+
if (resolved.error) {
|
|
48
|
+
options.spinner?.stop();
|
|
49
|
+
error(resolved.error);
|
|
50
|
+
process.exit(1);
|
|
51
|
+
}
|
|
52
|
+
if (resolved.status === "timeout") {
|
|
53
|
+
options.spinner?.stop();
|
|
54
|
+
error(`Polling timed out. Resume with: cawcut task status ${resolved.task_id} --wait`);
|
|
55
|
+
process.exit(1);
|
|
56
|
+
}
|
|
57
|
+
if (options.download && resolved.result_url) {
|
|
58
|
+
const target = typeof options.download === "string" ? options.download : undefined;
|
|
59
|
+
const dir = resolveDownloadDir(target);
|
|
60
|
+
options.spinner?.update("Downloading…");
|
|
61
|
+
resolved.local_path = await downloadFromUrl(resolved.result_url, dir, fetchImpl);
|
|
62
|
+
}
|
|
63
|
+
options.spinner?.stop();
|
|
64
|
+
if (options.json) {
|
|
65
|
+
success(JSON.stringify(resolved, null, 2));
|
|
66
|
+
return;
|
|
67
|
+
}
|
|
68
|
+
if (resolved.result_url) {
|
|
69
|
+
const lines = [resolved.result_url];
|
|
70
|
+
if (resolved.local_path)
|
|
71
|
+
lines.push(`Saved to: ${resolved.local_path}`);
|
|
72
|
+
success(lines.join("\n"));
|
|
73
|
+
return;
|
|
74
|
+
}
|
|
75
|
+
if (resolved.task_id) {
|
|
76
|
+
success(`Check status with: cawcut task status ${resolved.task_id} --wait`);
|
|
77
|
+
return;
|
|
78
|
+
}
|
|
79
|
+
error(`${actionLabel} completed without result_url or task_id.`);
|
|
80
|
+
process.exit(1);
|
|
81
|
+
}
|
|
82
|
+
//# sourceMappingURL=result-output.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"result-output.js","sourceRoot":"","sources":["../../src/lib/result-output.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,39 @@
|
|
|
1
|
+
const FRAMES = ["⠋", "⠙", "⠹", "⠸", "⠼", "⠴", "⠦", "⠧", "⠇", "⠏"];
|
|
2
|
+
export function startSpinner(initialMessage) {
|
|
3
|
+
if (!process.stderr.isTTY) {
|
|
4
|
+
process.stderr.write(`${initialMessage}\n`);
|
|
5
|
+
let last = initialMessage;
|
|
6
|
+
return {
|
|
7
|
+
update(msg) {
|
|
8
|
+
if (msg !== last) {
|
|
9
|
+
process.stderr.write(`${msg}\n`);
|
|
10
|
+
last = msg;
|
|
11
|
+
}
|
|
12
|
+
},
|
|
13
|
+
stop(finalMsg) {
|
|
14
|
+
if (finalMsg)
|
|
15
|
+
process.stderr.write(`${finalMsg}\n`);
|
|
16
|
+
},
|
|
17
|
+
};
|
|
18
|
+
}
|
|
19
|
+
let text = initialMessage;
|
|
20
|
+
let frame = 0;
|
|
21
|
+
const tick = () => {
|
|
22
|
+
process.stderr.write(`\r${FRAMES[frame % FRAMES.length]} ${text}`);
|
|
23
|
+
frame++;
|
|
24
|
+
};
|
|
25
|
+
tick();
|
|
26
|
+
const interval = setInterval(tick, 80);
|
|
27
|
+
return {
|
|
28
|
+
update(msg) {
|
|
29
|
+
text = msg;
|
|
30
|
+
},
|
|
31
|
+
stop(finalMsg) {
|
|
32
|
+
clearInterval(interval);
|
|
33
|
+
process.stderr.write("\r\x1b[K");
|
|
34
|
+
if (finalMsg)
|
|
35
|
+
process.stderr.write(`${finalMsg}\n`);
|
|
36
|
+
},
|
|
37
|
+
};
|
|
38
|
+
}
|
|
39
|
+
//# sourceMappingURL=spinner.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"spinner.js","sourceRoot":"","sources":["../../src/lib/spinner.ts"],"names":[],"mappings":"AAAA,MAAM,MAAM,GAAG,CAAC,GAAG,EAAE,GAAG,EAAE,GAAG,EAAE,GAAG,EAAE,GAAG,EAAE,GAAG,EAAE,GAAG,EAAE,GAAG,EAAE,GAAG,EAAE,GAAG,CAAC,CAAC;AAOlE,MAAM,UAAU,YAAY,CAAC,cAAsB;IACjD,IAAI,CAAC,OAAO,CAAC,MAAM,CAAC,KAAK,EAAE,CAAC;QAC1B,OAAO,CAAC,MAAM,CAAC,KAAK,CAAC,GAAG,cAAc,IAAI,CAAC,CAAC;QAC5C,IAAI,IAAI,GAAG,cAAc,CAAC;QAC1B,OAAO;YACL,MAAM,CAAC,GAAG;gBACR,IAAI,GAAG,KAAK,IAAI,EAAE,CAAC;oBACjB,OAAO,CAAC,MAAM,CAAC,KAAK,CAAC,GAAG,GAAG,IAAI,CAAC,CAAC;oBACjC,IAAI,GAAG,GAAG,CAAC;gBACb,CAAC;YACH,CAAC;YACD,IAAI,CAAC,QAAQ;gBACX,IAAI,QAAQ;oBAAE,OAAO,CAAC,MAAM,CAAC,KAAK,CAAC,GAAG,QAAQ,IAAI,CAAC,CAAC;YACtD,CAAC;SACF,CAAC;IACJ,CAAC;IAED,IAAI,IAAI,GAAG,cAAc,CAAC;IAC1B,IAAI,KAAK,GAAG,CAAC,CAAC;IAEd,MAAM,IAAI,GAAG,GAAG,EAAE;QAChB,OAAO,CAAC,MAAM,CAAC,KAAK,CAAC,KAAK,MAAM,CAAC,KAAK,GAAG,MAAM,CAAC,MAAM,CAAC,IAAI,IAAI,EAAE,CAAC,CAAC;QACnE,KAAK,EAAE,CAAC;IACV,CAAC,CAAC;IAEF,IAAI,EAAE,CAAC;IACP,MAAM,QAAQ,GAAG,WAAW,CAAC,IAAI,EAAE,EAAE,CAAC,CAAC;IAEvC,OAAO;QACL,MAAM,CAAC,GAAG;YACR,IAAI,GAAG,GAAG,CAAC;QACb,CAAC;QACD,IAAI,CAAC,QAAQ;YACX,aAAa,CAAC,QAAQ,CAAC,CAAC;YACxB,OAAO,CAAC,MAAM,CAAC,KAAK,CAAC,UAAU,CAAC,CAAC;YACjC,IAAI,QAAQ;gBAAE,OAAO,CAAC,MAAM,CAAC,KAAK,CAAC,GAAG,QAAQ,IAAI,CAAC,CAAC;QACtD,CAAC;KACF,CAAC;AACJ,CAAC"}
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
export interface GenerateValidationInput {
|
|
2
|
+
capability: string;
|
|
3
|
+
prompt?: string;
|
|
4
|
+
model?: string;
|
|
5
|
+
params?: Record<string, unknown>;
|
|
6
|
+
image?: string[];
|
|
7
|
+
video?: string[];
|
|
8
|
+
audio?: string[];
|
|
9
|
+
}
|
|
10
|
+
export interface ValidatedGenerateInput {
|
|
11
|
+
capability: string;
|
|
12
|
+
model: string;
|
|
13
|
+
params: Record<string, unknown>;
|
|
14
|
+
prompt: string;
|
|
15
|
+
mediaCounts: {
|
|
16
|
+
images: number;
|
|
17
|
+
videos: number;
|
|
18
|
+
audios: number;
|
|
19
|
+
};
|
|
20
|
+
}
|
|
21
|
+
export declare function validateGenerateInput(input: GenerateValidationInput): ValidatedGenerateInput;
|