@tolinax/ayoune-cli 2026.10.0 → 2026.11.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/lib/api/apiClient.js +219 -52
- package/lib/commands/_registry.js +5 -0
- package/lib/commands/aggregate/list.js +3 -2
- package/lib/commands/createActionsCommand.js +11 -10
- package/lib/commands/createAiCommand.js +10 -9
- package/lib/commands/createDbCommand.js +2 -1
- package/lib/commands/createDoctorCommand.js +305 -0
- package/lib/commands/createExportCommand.js +12 -11
- package/lib/commands/createJobsCommand.js +9 -8
- package/lib/commands/createMonitorCommand.js +20 -19
- package/lib/commands/createPermissionsCommand.js +12 -11
- package/lib/commands/createProgram.js +1 -1
- package/lib/commands/createSearchCommand.js +13 -12
- package/lib/commands/createServicesCommand.js +15 -12
- package/lib/commands/createSetupCommand.js +11 -13
- package/lib/commands/createSyncCommand.js +6 -5
- package/lib/commands/createTemplateCommand.js +8 -7
- package/lib/commands/createUsersCommand.js +6 -5
- package/lib/commands/createWebhooksCommand.js +4 -3
- package/lib/commands/deploy/alerts.js +2 -1
- package/lib/commands/deploy/clusters.js +2 -1
- package/lib/commands/deploy/deployments.js +4 -3
- package/lib/commands/deploy/pipelines.js +2 -1
- package/lib/commands/deploy/plans.js +3 -2
- package/lib/commands/deploy/pods.js +2 -1
- package/lib/commands/deploy/repos.js +3 -2
- package/lib/commands/functions/list.js +3 -2
- package/lib/commands/functions/logs.js +2 -1
- package/lib/commands/functions/versions.js +2 -1
- package/lib/commands/provision/hetzner.js +2 -1
- package/lib/helpers/printNextSteps.js +34 -0
- package/package.json +1 -1
package/lib/api/apiClient.js
CHANGED
|
@@ -1,5 +1,25 @@
|
|
|
1
|
-
|
|
1
|
+
// HTTP client for the `ay` CLI.
|
|
2
|
+
//
|
|
3
|
+
// Uses Node's built-in `fetch` (Node ≥ 18) — NO axios, NO @tolinax/ayoune-core
|
|
4
|
+
// HTTP wrapper. The `core/lib/http` re-export was previously used here, but
|
|
5
|
+
// it transitively requires `core/lib/aYOUne` at module top-level which loads
|
|
6
|
+
// the entire backend framework: mongoose schemas, prom-client metric
|
|
7
|
+
// registries, and an ioredis client wired to env vars that don't exist on
|
|
8
|
+
// end-user machines (causing visible "ERR_SOCKET_BAD_PORT" errors and a
|
|
9
|
+
// duplicate-index warning). The CLI is a pure HTTP client — it has no
|
|
10
|
+
// business loading any of that.
|
|
11
|
+
//
|
|
12
|
+
// The exported `api({...})` and `audit({...})` functions preserve the
|
|
13
|
+
// axios-shaped contract that callers expect:
|
|
14
|
+
//
|
|
15
|
+
// const res = await api({ baseURL, method, url, data, params, headers });
|
|
16
|
+
// res.status / res.statusText / res.data / res.headers
|
|
17
|
+
//
|
|
18
|
+
// Errors throw with `.response = { status, statusText, data, headers }` and
|
|
19
|
+
// `.config = { baseURL, url, method }` so `handleAPIError` keeps working
|
|
20
|
+
// unchanged.
|
|
2
21
|
import chalk from "chalk";
|
|
22
|
+
import { Readable } from "stream";
|
|
3
23
|
import { config } from "../helpers/config.js";
|
|
4
24
|
let debugEnabled = false;
|
|
5
25
|
export function enableDebug() {
|
|
@@ -22,57 +42,211 @@ const MODULE_HOST_OVERRIDES = {
|
|
|
22
42
|
export function getModuleBaseUrl(module) {
|
|
23
43
|
return MODULE_HOST_OVERRIDES[module] || `https://${module}-api.ayoune.app`;
|
|
24
44
|
}
|
|
25
|
-
const
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
45
|
+
const DEFAULT_TIMEOUT = 30000;
|
|
46
|
+
const MAX_RETRIES = 3;
|
|
47
|
+
/**
|
|
48
|
+
* Build the full request URL from baseURL + url + serialized params.
|
|
49
|
+
* Handles the same edge cases as axios: collapses duplicate slashes,
|
|
50
|
+
* preserves the protocol's `://`, and skips falsy params.
|
|
51
|
+
*/
|
|
52
|
+
function buildUrl(req) {
|
|
53
|
+
const base = req.baseURL || "";
|
|
54
|
+
const path = req.url || "";
|
|
55
|
+
let combined = `${base}/${path}`.replace(/\/+/g, "/").replace(":/", "://");
|
|
56
|
+
if (req.params && typeof req.params === "object") {
|
|
57
|
+
const usp = new URLSearchParams();
|
|
58
|
+
for (const [k, v] of Object.entries(req.params)) {
|
|
59
|
+
if (v === undefined || v === null)
|
|
60
|
+
continue;
|
|
61
|
+
// Booleans / numbers stringify cleanly; arrays get repeated keys; objects get JSON.
|
|
62
|
+
if (Array.isArray(v)) {
|
|
63
|
+
v.forEach((item) => usp.append(k, String(item)));
|
|
64
|
+
}
|
|
65
|
+
else if (typeof v === "object") {
|
|
66
|
+
usp.append(k, JSON.stringify(v));
|
|
67
|
+
}
|
|
68
|
+
else {
|
|
69
|
+
usp.append(k, String(v));
|
|
36
70
|
}
|
|
37
71
|
}
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
}
|
|
51
|
-
}
|
|
72
|
+
const qs = usp.toString();
|
|
73
|
+
if (qs)
|
|
74
|
+
combined += (combined.includes("?") ? "&" : "?") + qs;
|
|
75
|
+
}
|
|
76
|
+
return combined;
|
|
77
|
+
}
|
|
78
|
+
/** Build a "configured" request — used by api()/audit() to apply defaults. */
|
|
79
|
+
function withDefaults(defaults, req) {
|
|
80
|
+
return {
|
|
81
|
+
...defaults,
|
|
82
|
+
...req,
|
|
83
|
+
headers: { ...(defaults.headers || {}), ...(req.headers || {}) },
|
|
84
|
+
};
|
|
85
|
+
}
|
|
86
|
+
/** Sleep for n ms — used by retry backoff. */
|
|
87
|
+
function sleep(ms) {
|
|
88
|
+
return new Promise((resolve) => setTimeout(resolve, ms));
|
|
89
|
+
}
|
|
90
|
+
/** Decide whether to retry given the last error. */
|
|
91
|
+
function shouldRetry(err) {
|
|
92
|
+
// No response → network or timeout error → retry.
|
|
93
|
+
if (!err.response)
|
|
94
|
+
return true;
|
|
95
|
+
const status = err.response.status;
|
|
96
|
+
return status >= 500 || status === 429;
|
|
97
|
+
}
|
|
98
|
+
/** Compute backoff in ms, honouring Retry-After on 429. */
|
|
99
|
+
function backoffMs(attempt, err) {
|
|
100
|
+
var _a, _b, _c;
|
|
101
|
+
const status = (_a = err.response) === null || _a === void 0 ? void 0 : _a.status;
|
|
102
|
+
const retryAfter = (_c = (_b = err.response) === null || _b === void 0 ? void 0 : _b.headers) === null || _c === void 0 ? void 0 : _c["retry-after"];
|
|
103
|
+
if (status === 429 && retryAfter) {
|
|
104
|
+
const seconds = parseInt(retryAfter, 10);
|
|
105
|
+
if (!Number.isNaN(seconds)) {
|
|
106
|
+
debugLog(chalk.yellow(`Rate limited. Retrying in ${seconds}s...`));
|
|
107
|
+
return seconds * 1000;
|
|
108
|
+
}
|
|
109
|
+
}
|
|
110
|
+
// Exponential: 1s, 2s, 4s
|
|
111
|
+
const delay = Math.pow(2, attempt) * 1000;
|
|
112
|
+
debugLog(chalk.yellow(`Retrying in ${delay / 1000}s (attempt ${attempt + 1}/${MAX_RETRIES})...`));
|
|
113
|
+
return delay;
|
|
114
|
+
}
|
|
115
|
+
/**
|
|
116
|
+
* Headers#entries() returned by undici returns a flat record. Normalize to
|
|
117
|
+
* a plain `Record<string, string>` so callers (and handleAPIError) can index
|
|
118
|
+
* by header name without thinking about Headers semantics.
|
|
119
|
+
*/
|
|
120
|
+
function headersToObject(headers) {
|
|
121
|
+
const out = {};
|
|
122
|
+
headers.forEach((value, key) => {
|
|
123
|
+
out[key.toLowerCase()] = value;
|
|
124
|
+
});
|
|
125
|
+
return out;
|
|
126
|
+
}
|
|
127
|
+
/**
|
|
128
|
+
* Single-shot fetch — no retry. Throws axios-shaped errors so the existing
|
|
129
|
+
* `handleAPIError` works unchanged.
|
|
130
|
+
*/
|
|
131
|
+
async function performRequest(req) {
|
|
132
|
+
var _a;
|
|
133
|
+
const method = (req.method || "GET").toUpperCase();
|
|
134
|
+
const url = buildUrl(req);
|
|
135
|
+
const headers = { ...(req.headers || {}) };
|
|
136
|
+
let body;
|
|
137
|
+
if (req.data !== undefined && req.data !== null && method !== "GET" && method !== "HEAD") {
|
|
138
|
+
body = typeof req.data === "string" ? req.data : JSON.stringify(req.data);
|
|
139
|
+
if (!headers["Content-Type"] && !headers["content-type"]) {
|
|
140
|
+
headers["Content-Type"] = "application/json";
|
|
141
|
+
}
|
|
142
|
+
}
|
|
143
|
+
const timeout = (_a = req.timeout) !== null && _a !== void 0 ? _a : DEFAULT_TIMEOUT;
|
|
144
|
+
const controller = new AbortController();
|
|
145
|
+
const timer = setTimeout(() => controller.abort(), timeout);
|
|
146
|
+
let response;
|
|
147
|
+
try {
|
|
148
|
+
response = await fetch(url, {
|
|
149
|
+
method,
|
|
150
|
+
headers,
|
|
151
|
+
body,
|
|
152
|
+
signal: controller.signal,
|
|
153
|
+
});
|
|
154
|
+
}
|
|
155
|
+
catch (e) {
|
|
156
|
+
clearTimeout(timer);
|
|
157
|
+
const err = new Error(e.name === "AbortError" ? `Request timed out after ${timeout}ms` : e.message);
|
|
158
|
+
err.code = e.code;
|
|
159
|
+
err.config = { baseURL: req.baseURL, url: req.url, method };
|
|
160
|
+
throw err;
|
|
161
|
+
}
|
|
162
|
+
clearTimeout(timer);
|
|
163
|
+
// Stream mode: hand back a Node Readable wrapping the fetch body. Used by
|
|
164
|
+
// searchClient.searchGlobal() for the SSE endpoint. The caller .on('data')s
|
|
165
|
+
// it directly, no JSON parsing.
|
|
166
|
+
let data = undefined;
|
|
167
|
+
if (req.responseType === "stream" && response.body) {
|
|
168
|
+
// Readable.fromWeb is available since Node 18.
|
|
169
|
+
data = Readable.fromWeb(response.body);
|
|
170
|
+
}
|
|
171
|
+
else {
|
|
172
|
+
// Parse body — JSON if Content-Type says so, else text. Empty body → undefined.
|
|
173
|
+
const ct = response.headers.get("content-type") || "";
|
|
174
|
+
const text = await response.text();
|
|
175
|
+
if (text.length > 0) {
|
|
176
|
+
if (ct.includes("application/json")) {
|
|
177
|
+
try {
|
|
178
|
+
data = JSON.parse(text);
|
|
179
|
+
}
|
|
180
|
+
catch (_b) {
|
|
181
|
+
data = text;
|
|
182
|
+
}
|
|
183
|
+
}
|
|
184
|
+
else {
|
|
185
|
+
data = text;
|
|
186
|
+
}
|
|
187
|
+
}
|
|
188
|
+
}
|
|
189
|
+
const apiResponse = {
|
|
190
|
+
status: response.status,
|
|
191
|
+
statusText: response.statusText,
|
|
192
|
+
data,
|
|
193
|
+
headers: headersToObject(response.headers),
|
|
194
|
+
config: { baseURL: req.baseURL, url: req.url, method },
|
|
195
|
+
};
|
|
196
|
+
if (response.status >= 200 && response.status < 300) {
|
|
197
|
+
return apiResponse;
|
|
198
|
+
}
|
|
199
|
+
// Non-2xx → throw axios-shaped error.
|
|
200
|
+
const err = new Error(`HTTP ${response.status} ${response.statusText}`);
|
|
201
|
+
err.response = apiResponse;
|
|
202
|
+
err.config = apiResponse.config;
|
|
203
|
+
throw err;
|
|
204
|
+
}
|
|
205
|
+
/**
|
|
206
|
+
* Retry wrapper around `performRequest`. Mirrors the previous axios-retry
|
|
207
|
+
* config: 3 retries, exponential backoff (1s/2s/4s), respects Retry-After on
|
|
208
|
+
* 429, retries on network errors and 5xx/429 status codes.
|
|
209
|
+
*/
|
|
210
|
+
async function performRequestWithRetry(req) {
|
|
211
|
+
let lastErr;
|
|
212
|
+
for (let attempt = 0; attempt <= MAX_RETRIES; attempt++) {
|
|
213
|
+
try {
|
|
214
|
+
return await performRequest(req);
|
|
215
|
+
}
|
|
216
|
+
catch (err) {
|
|
217
|
+
lastErr = err;
|
|
218
|
+
if (attempt >= MAX_RETRIES || !shouldRetry(err))
|
|
219
|
+
throw err;
|
|
220
|
+
await sleep(backoffMs(attempt, err));
|
|
221
|
+
}
|
|
222
|
+
}
|
|
223
|
+
throw lastErr;
|
|
224
|
+
}
|
|
52
225
|
/**
|
|
53
|
-
* Debug-aware
|
|
54
|
-
*
|
|
226
|
+
* Debug-aware request wrapper. Logs request/response details to stderr when
|
|
227
|
+
* debug mode is enabled, then delegates to the retry wrapper.
|
|
55
228
|
*/
|
|
56
|
-
async function debugRequest(
|
|
229
|
+
async function debugRequest(defaults, requestConfig) {
|
|
57
230
|
var _a, _b, _c, _d;
|
|
58
|
-
const
|
|
59
|
-
const
|
|
231
|
+
const merged = withDefaults(defaults, requestConfig);
|
|
232
|
+
const url = buildUrl(merged);
|
|
233
|
+
const method = (merged.method || "GET").toUpperCase();
|
|
60
234
|
if (debugEnabled) {
|
|
61
235
|
debugLog(`${chalk.yellow(method)} ${chalk.cyan(url)}`);
|
|
62
|
-
if (
|
|
63
|
-
debugLog("params:", JSON.stringify(
|
|
236
|
+
if (merged.params && Object.keys(merged.params).length) {
|
|
237
|
+
debugLog("params:", JSON.stringify(merged.params));
|
|
64
238
|
}
|
|
65
|
-
if (
|
|
66
|
-
debugLog("body:", JSON.stringify(
|
|
239
|
+
if (merged.data) {
|
|
240
|
+
debugLog("body:", JSON.stringify(merged.data).substring(0, 200));
|
|
67
241
|
}
|
|
68
|
-
const auth = ((_a =
|
|
242
|
+
const auth = ((_a = merged.headers) === null || _a === void 0 ? void 0 : _a.Authorization) || ((_b = merged.headers) === null || _b === void 0 ? void 0 : _b.authorization);
|
|
69
243
|
if (auth) {
|
|
70
244
|
const token = String(auth);
|
|
71
245
|
debugLog("auth:", token.substring(0, 15) + "..." + token.substring(token.length - 10));
|
|
72
246
|
}
|
|
73
247
|
}
|
|
74
248
|
try {
|
|
75
|
-
const res = await
|
|
249
|
+
const res = await performRequestWithRetry(merged);
|
|
76
250
|
if (debugEnabled) {
|
|
77
251
|
const meta = (_c = res.data) === null || _c === void 0 ? void 0 : _c.meta;
|
|
78
252
|
const total = (_d = meta === null || meta === void 0 ? void 0 : meta.pageInfo) === null || _d === void 0 ? void 0 : _d.totalEntries;
|
|
@@ -102,20 +276,13 @@ async function debugRequest(client, requestConfig) {
|
|
|
102
276
|
throw err;
|
|
103
277
|
}
|
|
104
278
|
}
|
|
105
|
-
const
|
|
106
|
-
|
|
107
|
-
retry: retryConfig,
|
|
108
|
-
logging: false,
|
|
109
|
-
metrics: false,
|
|
110
|
-
});
|
|
111
|
-
const auditClient = http.create({
|
|
279
|
+
const API_DEFAULTS = { timeout: DEFAULT_TIMEOUT };
|
|
280
|
+
const AUDIT_DEFAULTS = {
|
|
112
281
|
baseURL: config.auditUrl,
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
/** Callable
|
|
118
|
-
const
|
|
119
|
-
/** Callable audit client that supports debug logging */
|
|
120
|
-
const audit = (requestConfig) => debugRequest(auditClient, requestConfig);
|
|
282
|
+
timeout: DEFAULT_TIMEOUT,
|
|
283
|
+
};
|
|
284
|
+
/** Callable API client that supports debug logging. */
|
|
285
|
+
const api = (requestConfig) => debugRequest(API_DEFAULTS, requestConfig);
|
|
286
|
+
/** Callable audit client that supports debug logging. */
|
|
287
|
+
const audit = (requestConfig) => debugRequest(AUDIT_DEFAULTS, requestConfig);
|
|
121
288
|
export { api, audit };
|
|
@@ -240,6 +240,11 @@ export const COMMAND_REGISTRY = [
|
|
|
240
240
|
description: "Provision aYOUne to a cloud provider (AWS / GCP / Azure / DigitalOcean / Hetzner)",
|
|
241
241
|
loader: async () => (await import("./provision/index.js")).createProvisionCommand,
|
|
242
242
|
},
|
|
243
|
+
{
|
|
244
|
+
name: "doctor",
|
|
245
|
+
description: "Run a health check on your local aYOUne environment",
|
|
246
|
+
loader: async () => (await import("./createDoctorCommand.js")).createDoctorCommand,
|
|
247
|
+
},
|
|
243
248
|
{
|
|
244
249
|
name: "context",
|
|
245
250
|
aliases: ["ctx"],
|
|
@@ -9,13 +9,14 @@ import { handleResponseFormatOptions } from "../../helpers/handleResponseFormatO
|
|
|
9
9
|
import { spinner } from "../../../index.js";
|
|
10
10
|
import { EXIT_GENERAL_ERROR } from "../../exitCodes.js";
|
|
11
11
|
import { cliError } from "../../helpers/cliError.js";
|
|
12
|
+
import { parseInteger } from "../../helpers/parseInt.js";
|
|
12
13
|
export function addListSubcommand(agg, rootProgram) {
|
|
13
14
|
agg
|
|
14
15
|
.command("list")
|
|
15
16
|
.alias("ls")
|
|
16
17
|
.description("List saved aggregation queries")
|
|
17
|
-
.option("-l, --limit <number>", "Limit results",
|
|
18
|
-
.option("-p, --page <number>", "Page number",
|
|
18
|
+
.option("-l, --limit <number>", "Limit results", parseInteger, 50)
|
|
19
|
+
.option("-p, --page <number>", "Page number", parseInteger, 1)
|
|
19
20
|
.option("--search <term>", "Search by name")
|
|
20
21
|
.action(async (options) => {
|
|
21
22
|
var _a, _b, _c, _d, _e;
|
|
@@ -5,19 +5,20 @@ import { secureStorage } from "../helpers/secureStorage.js";
|
|
|
5
5
|
import { spinner } from "../../index.js";
|
|
6
6
|
import { EXIT_GENERAL_ERROR, EXIT_PERMISSION_DENIED } from "../exitCodes.js";
|
|
7
7
|
import { cliError } from "../helpers/cliError.js";
|
|
8
|
+
import { parseInteger } from "../helpers/parseInt.js";
|
|
8
9
|
export function createActionsCommand(program) {
|
|
9
10
|
program
|
|
10
11
|
.command("actions [search]")
|
|
11
12
|
.alias("act")
|
|
12
13
|
.description("Discover registered API actions from all services")
|
|
13
|
-
.addHelpText("after", `
|
|
14
|
-
Examples:
|
|
15
|
-
ay actions List all API actions (interactive)
|
|
16
|
-
ay actions generate Search actions matching "generate"
|
|
17
|
-
ay actions --namespace ai List all AI actions
|
|
18
|
-
ay actions --host ai.ayoune.app List actions for a specific host
|
|
19
|
-
ay actions --method POST List only POST actions
|
|
20
|
-
ay actions --list-namespaces List all available namespaces
|
|
14
|
+
.addHelpText("after", `
|
|
15
|
+
Examples:
|
|
16
|
+
ay actions List all API actions (interactive)
|
|
17
|
+
ay actions generate Search actions matching "generate"
|
|
18
|
+
ay actions --namespace ai List all AI actions
|
|
19
|
+
ay actions --host ai.ayoune.app List actions for a specific host
|
|
20
|
+
ay actions --method POST List only POST actions
|
|
21
|
+
ay actions --list-namespaces List all available namespaces
|
|
21
22
|
ay actions -r table --columns "operationId,method,endpoint,host"`)
|
|
22
23
|
.option("--namespace <ns>", "Filter by namespace (e.g. ai, crm, pm)")
|
|
23
24
|
.option("--host <host>", "Filter by host")
|
|
@@ -25,8 +26,8 @@ Examples:
|
|
|
25
26
|
.option("--capability <cap>", "Filter by capability")
|
|
26
27
|
.option("--list-namespaces", "List all unique namespaces")
|
|
27
28
|
.option("--list-hosts", "List all unique hosts")
|
|
28
|
-
.option("-p, --page <number>", "Page",
|
|
29
|
-
.option("-l, --limit <number>", "Limit",
|
|
29
|
+
.option("-p, --page <number>", "Page", parseInteger, 1)
|
|
30
|
+
.option("-l, --limit <number>", "Limit", parseInteger, 100)
|
|
30
31
|
.action(async (search, options) => {
|
|
31
32
|
var _a, _b, _c;
|
|
32
33
|
try {
|
|
@@ -7,6 +7,7 @@ import { spinner } from "../../index.js";
|
|
|
7
7
|
import { EXIT_GENERAL_ERROR } from "../exitCodes.js";
|
|
8
8
|
import { cliError } from "../helpers/cliError.js";
|
|
9
9
|
import { getContextSummary, getContextForAI, hasActiveContext } from "../helpers/contextInjector.js";
|
|
10
|
+
import { parseInteger } from "../helpers/parseInt.js";
|
|
10
11
|
const AI_HOST = "ai.ayoune.app";
|
|
11
12
|
export function createAiCommand(program) {
|
|
12
13
|
const ai = program
|
|
@@ -51,8 +52,8 @@ export function createAiCommand(program) {
|
|
|
51
52
|
// ay ai conversations — list AI conversations
|
|
52
53
|
ai.command("conversations")
|
|
53
54
|
.description("List AI conversations")
|
|
54
|
-
.option("-l, --limit <number>", "Limit",
|
|
55
|
-
.option("-p, --page <number>", "Page",
|
|
55
|
+
.option("-l, --limit <number>", "Limit", parseInteger, 20)
|
|
56
|
+
.option("-p, --page <number>", "Page", parseInteger, 1)
|
|
56
57
|
.action(async (options) => {
|
|
57
58
|
var _a, _b, _c;
|
|
58
59
|
try {
|
|
@@ -79,8 +80,8 @@ export function createAiCommand(program) {
|
|
|
79
80
|
// ay ai prompts — list AI prompt templates
|
|
80
81
|
ai.command("prompts")
|
|
81
82
|
.description("List AI prompt templates")
|
|
82
|
-
.option("-l, --limit <number>", "Limit",
|
|
83
|
-
.option("-p, --page <number>", "Page",
|
|
83
|
+
.option("-l, --limit <number>", "Limit", parseInteger, 20)
|
|
84
|
+
.option("-p, --page <number>", "Page", parseInteger, 1)
|
|
84
85
|
.action(async (options) => {
|
|
85
86
|
var _a, _b, _c;
|
|
86
87
|
try {
|
|
@@ -141,11 +142,11 @@ export function createAiCommand(program) {
|
|
|
141
142
|
// ay ai generate <type> <prompt> — generate content
|
|
142
143
|
ai.command("generate <type> <prompt...>")
|
|
143
144
|
.description("Generate content (text, image, video)")
|
|
144
|
-
.addHelpText("after", `
|
|
145
|
-
Types: text, image, video
|
|
146
|
-
|
|
147
|
-
Examples:
|
|
148
|
-
ay ai generate text "Write a product description for..."
|
|
145
|
+
.addHelpText("after", `
|
|
146
|
+
Types: text, image, video
|
|
147
|
+
|
|
148
|
+
Examples:
|
|
149
|
+
ay ai generate text "Write a product description for..."
|
|
149
150
|
ay ai generate image "A modern office building"`)
|
|
150
151
|
.action(async (type, promptWords, options) => {
|
|
151
152
|
try {
|
|
@@ -6,6 +6,7 @@ import { cliError } from "../helpers/cliError.js";
|
|
|
6
6
|
import { handleResponseFormatOptions } from "../helpers/handleResponseFormatOptions.js";
|
|
7
7
|
import { apiCallHandler } from "../api/apiCallHandler.js";
|
|
8
8
|
import { initializeSettings } from "../helpers/initializeSettings.js";
|
|
9
|
+
import { parseInteger } from "../helpers/parseInt.js";
|
|
9
10
|
function maskUri(uri) {
|
|
10
11
|
return uri.replace(/:\/\/([^:]+):([^@]+)@/, "://***:***@");
|
|
11
12
|
}
|
|
@@ -97,7 +98,7 @@ Examples:
|
|
|
97
98
|
.command("list")
|
|
98
99
|
.alias("ls")
|
|
99
100
|
.description("List configured database targets")
|
|
100
|
-
.option("-l, --limit <number>", "Limit results",
|
|
101
|
+
.option("-l, --limit <number>", "Limit results", parseInteger, 50)
|
|
101
102
|
.action(async (options) => {
|
|
102
103
|
var _a, _b, _c, _d, _e;
|
|
103
104
|
try {
|