@tolinax/ayoune-cli 2026.7.2 → 2026.8.1

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.
@@ -1281,6 +1281,12 @@ const modelsAndRights = [
1281
1281
  module: "config",
1282
1282
  right: "config.datapools",
1283
1283
  },
1284
+ {
1285
+ plural: "DataTargets",
1286
+ singular: "DataTarget",
1287
+ module: "config",
1288
+ right: "config.datatargets",
1289
+ },
1284
1290
  {
1285
1291
  plural: "Decisions",
1286
1292
  singular: "Decision",
package/index.js CHANGED
@@ -1,11 +1,9 @@
1
1
  #! /usr/bin/env node
2
2
  import { Command } from "commander";
3
3
  import { createSpinner } from "nanospinner";
4
- import { initializeSettings } from "./lib/helpers/initializeSettings.js";
5
4
  import { createProgram } from "./lib/commands/createProgram.js";
6
5
  //Create new command instance
7
6
  const program = new Command();
8
- initializeSettings();
9
7
  //Setup spinner - output to stderr so stdout stays clean for piping
10
8
  export const spinner = createSpinner("Getting data...", { stream: process.stderr });
11
9
  createProgram(program);
@@ -6,6 +6,7 @@ import { spinner } from "../../index.js";
6
6
  import { EXIT_GENERAL_ERROR, EXIT_MISUSE } from "../exitCodes.js";
7
7
  import { cliError } from "../helpers/cliError.js";
8
8
  import { readPipelineInput } from "../helpers/readPipelineInput.js";
9
+ import { initializeSettings } from "../helpers/initializeSettings.js";
9
10
  function wrapAggResult(res) {
10
11
  if (Array.isArray(res)) {
11
12
  return { payload: res, meta: { resultCount: res.length } };
@@ -252,6 +253,7 @@ Examples:
252
253
  cliError("Wizard requires an interactive terminal (TTY)", EXIT_MISUSE);
253
254
  }
254
255
  // Step 1: Select model
256
+ initializeSettings();
255
257
  spinner.start({ text: "Loading models...", color: "magenta" });
256
258
  const modelsRes = await apiCallHandler("aggregation", "models", "get");
257
259
  spinner.stop();
@@ -5,6 +5,7 @@ import { EXIT_GENERAL_ERROR, EXIT_MISUSE } from "../exitCodes.js";
5
5
  import { cliError } from "../helpers/cliError.js";
6
6
  import { handleResponseFormatOptions } from "../helpers/handleResponseFormatOptions.js";
7
7
  import { apiCallHandler } from "../api/apiCallHandler.js";
8
+ import { initializeSettings } from "../helpers/initializeSettings.js";
8
9
  function maskUri(uri) {
9
10
  return uri.replace(/:\/\/([^:]+):([^@]+)@/, "://***:***@");
10
11
  }
@@ -126,7 +127,6 @@ Examples:
126
127
  .option("--collection <col>", "Default target collection name")
127
128
  .option("--write-mode <mode>", "Write mode: insert, upsert, replace", "insert")
128
129
  .action(async (options) => {
129
- var _a;
130
130
  try {
131
131
  const opts = { ...program.opts(), ...options };
132
132
  let { name, type, uri, database, collection, writeMode } = opts;
@@ -159,7 +159,8 @@ Examples:
159
159
  const res = await apiCallHandler("config", "datatargets", "post", body, {
160
160
  responseFormat: opts.responseFormat,
161
161
  });
162
- const slug = ((_a = res.payload) === null || _a === void 0 ? void 0 : _a.slug) || name;
162
+ const created = Array.isArray(res.payload) ? res.payload[0] : res.payload;
163
+ const slug = (created === null || created === void 0 ? void 0 : created.slug) || name;
163
164
  spinner.success({ text: `Target "${slug}" created — use with: ay db copy ${slug}` });
164
165
  }
165
166
  catch (e) {
@@ -239,6 +240,7 @@ async function resolveQuery(slugOrId) {
239
240
  return Array.isArray(entries) ? entries[0] : entries;
240
241
  }
241
242
  async function promptTarget() {
243
+ initializeSettings();
242
244
  spinner.start({ text: "Loading targets...", color: "cyan" });
243
245
  const res = await apiCallHandler("config", "datatargets", "get", null, { limit: 100, active: true });
244
246
  spinner.stop();
@@ -256,6 +258,7 @@ async function promptTarget() {
256
258
  return selected;
257
259
  }
258
260
  async function promptQuery() {
261
+ initializeSettings();
259
262
  spinner.start({ text: "Loading queries...", color: "magenta" });
260
263
  const res = await apiCallHandler("config", "queries", "get", null, {
261
264
  limit: 100,
@@ -1,11 +1,19 @@
1
1
  //Initializes settings for system environment and inquirer
2
- import inquirer from "inquirer";
3
- import inquirerFuzzyPath from "inquirer-fuzzy-path";
4
- import inquirerSearchList from "inquirer-search-list";
5
- import inquirerFileTreeSelection from "inquirer-file-tree-selection-prompt";
6
- import inquirerTableInput from "inquirer-table-input";
7
- import InterruptedPrompt from "inquirer-interrupted-prompt";
2
+ // Lazy-loaded: inquirer plugins are heavy (~4000 modules incl. rxjs).
3
+ // Only register them when an interactive prompt is actually needed.
4
+ import { createRequire } from "module";
5
+ let _initialized = false;
8
6
  export function initializeSettings() {
7
+ if (_initialized)
8
+ return;
9
+ _initialized = true;
10
+ const require = createRequire(import.meta.url);
11
+ const inquirer = require("inquirer");
12
+ const inquirerFuzzyPath = require("inquirer-fuzzy-path");
13
+ const inquirerSearchList = require("inquirer-search-list");
14
+ const inquirerFileTreeSelection = require("inquirer-file-tree-selection-prompt");
15
+ const inquirerTableInput = require("inquirer-table-input");
16
+ const InterruptedPrompt = require("inquirer-interrupted-prompt");
9
17
  inquirer.registerPrompt("fuzzypath", inquirerFuzzyPath);
10
18
  inquirer.registerPrompt("search-list", inquirerSearchList);
11
19
  inquirer.registerPrompt("file-tree-selection", inquirerFileTreeSelection);
@@ -2,8 +2,10 @@ import inquirer from "inquirer";
2
2
  import chalk from "chalk";
3
3
  import { apiCallHandler } from "../api/apiCallHandler.js";
4
4
  import { spinner } from "../../index.js";
5
+ import { initializeSettings } from "../helpers/initializeSettings.js";
5
6
  export async function handleEditOperation(module, collection, editContent) {
6
7
  console.log(editContent);
8
+ initializeSettings();
7
9
  const { edits } = await inquirer.prompt([
8
10
  {
9
11
  type: "table-input",
@@ -1,4 +1,5 @@
1
1
  import inquirer from "inquirer";
2
+ import { initializeSettings } from "../helpers/initializeSettings.js";
2
3
  function getEntryLabel(el) {
3
4
  const name = el.name || el.title || el.subject || el.label || el.originalname || el.summary;
4
5
  if (name) {
@@ -7,6 +8,7 @@ function getEntryLabel(el) {
7
8
  return el._id;
8
9
  }
9
10
  export async function promptEntry(result) {
11
+ initializeSettings();
10
12
  const { entry } = await inquirer.prompt([
11
13
  {
12
14
  type: "search-list",
@@ -1,7 +1,9 @@
1
1
  import inquirer from "inquirer";
2
2
  import os from "os";
3
3
  import path from "path";
4
+ import { initializeSettings } from "../helpers/initializeSettings.js";
4
5
  export async function promptFilePath(fp) {
6
+ initializeSettings();
5
7
  const { filePath } = await inquirer.prompt([
6
8
  {
7
9
  type: "fuzzypath",
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@tolinax/ayoune-cli",
3
- "version": "2026.7.2",
3
+ "version": "2026.8.1",
4
4
  "description": "CLI for the aYOUne Business-as-a-Service platform",
5
5
  "type": "module",
6
6
  "main": "./index.js",
@@ -1,88 +0,0 @@
1
- import { readFileSync, writeFileSync, existsSync, mkdirSync } from "fs";
2
- import path from "path";
3
- import os from "os";
4
- import crypto from "crypto";
5
- const CONFIG_PATH = path.join(os.homedir(), ".config", "ayoune", "db-copies.json");
6
- const ALGORITHM = "aes-256-cbc";
7
- function deriveKey() {
8
- const seed = `ayoune-cli:${os.hostname()}:${os.userInfo().username}`;
9
- return crypto.createHash("sha256").update(seed).digest();
10
- }
11
- function encrypt(text) {
12
- const key = deriveKey();
13
- const iv = crypto.randomBytes(16);
14
- const cipher = crypto.createCipheriv(ALGORITHM, key, iv);
15
- let encrypted = cipher.update(text, "utf8", "hex");
16
- encrypted += cipher.final("hex");
17
- return iv.toString("hex") + ":" + encrypted;
18
- }
19
- function decrypt(data) {
20
- const key = deriveKey();
21
- const [ivHex, encrypted] = data.split(":");
22
- if (!ivHex || !encrypted)
23
- return data;
24
- try {
25
- const iv = Buffer.from(ivHex, "hex");
26
- const decipher = crypto.createDecipheriv(ALGORITHM, key, iv);
27
- let decrypted = decipher.update(encrypted, "hex", "utf8");
28
- decrypted += decipher.final("utf8");
29
- return decrypted;
30
- }
31
- catch (_a) {
32
- return data;
33
- }
34
- }
35
- export function maskUri(uri) {
36
- return uri.replace(/:\/\/([^:]+):([^@]+)@/, "://***:***@");
37
- }
38
- export function loadCopyConfigs() {
39
- if (!existsSync(CONFIG_PATH))
40
- return [];
41
- try {
42
- return JSON.parse(readFileSync(CONFIG_PATH, "utf-8"));
43
- }
44
- catch (_a) {
45
- return [];
46
- }
47
- }
48
- export function saveCopyConfigs(configs) {
49
- const dir = path.dirname(CONFIG_PATH);
50
- if (!existsSync(dir))
51
- mkdirSync(dir, { recursive: true });
52
- writeFileSync(CONFIG_PATH, JSON.stringify(configs, null, 2), "utf-8");
53
- }
54
- export function addCopyConfig(config) {
55
- const configs = loadCopyConfigs();
56
- // Encrypt URIs before saving
57
- config.fromUri = encrypt(config.fromUri);
58
- config.toUri = encrypt(config.toUri);
59
- configs.push(config);
60
- saveCopyConfigs(configs);
61
- }
62
- export function removeCopyConfig(id) {
63
- const configs = loadCopyConfigs();
64
- const filtered = configs.filter((c) => c.id !== id);
65
- if (filtered.length === configs.length)
66
- return false;
67
- saveCopyConfigs(filtered);
68
- return true;
69
- }
70
- export function getDecryptedConfig(config) {
71
- return {
72
- fromUri: decrypt(config.fromUri),
73
- toUri: decrypt(config.toUri),
74
- };
75
- }
76
- export function updateCopyConfigLastRun(id, status, error) {
77
- const configs = loadCopyConfigs();
78
- const config = configs.find((c) => c.id === id);
79
- if (config) {
80
- config.lastRun = new Date().toISOString();
81
- config.lastStatus = status;
82
- if (error)
83
- config.lastError = error;
84
- else
85
- delete config.lastError;
86
- saveCopyConfigs(configs);
87
- }
88
- }
@@ -1,87 +0,0 @@
1
- import { MongoClient } from "mongodb";
2
- /**
3
- * Writes aggregation query results to a target MongoDB collection.
4
- * This is the core of the copy flow: query results from the aggregation API
5
- * are written to an external MongoDB target.
6
- */
7
- export async function writeToTarget(targetUri, database, collection, docs, writeMode, batchSize = 1000) {
8
- const startTime = Date.now();
9
- const client = new MongoClient(targetUri);
10
- let written = 0;
11
- let errors = 0;
12
- try {
13
- await client.connect();
14
- const db = database ? client.db(database) : client.db();
15
- const col = db.collection(collection);
16
- if (writeMode === "replace") {
17
- try {
18
- await col.drop();
19
- }
20
- catch (_a) {
21
- // Collection may not exist
22
- }
23
- }
24
- // Write in batches
25
- for (let i = 0; i < docs.length; i += batchSize) {
26
- const batch = docs.slice(i, i + batchSize);
27
- const result = await writeBatch(col, batch, writeMode === "upsert");
28
- written += result.written;
29
- errors += result.errors;
30
- }
31
- return { written, errors, duration: Date.now() - startTime };
32
- }
33
- finally {
34
- await client.close();
35
- }
36
- }
37
- async function writeBatch(collection, docs, upsert) {
38
- var _a, _b, _c, _d;
39
- try {
40
- if (upsert) {
41
- const ops = docs.map((doc) => ({
42
- updateOne: {
43
- filter: { _id: doc._id },
44
- update: { $set: doc },
45
- upsert: true,
46
- },
47
- }));
48
- const result = await collection.bulkWrite(ops, { ordered: false });
49
- return { written: (result.upsertedCount || 0) + (result.modifiedCount || 0), errors: 0 };
50
- }
51
- else {
52
- const result = await collection.insertMany(docs, { ordered: false });
53
- return { written: result.insertedCount, errors: 0 };
54
- }
55
- }
56
- catch (e) {
57
- const written = (_d = (_b = (_a = e.result) === null || _a === void 0 ? void 0 : _a.nInserted) !== null && _b !== void 0 ? _b : (_c = e.result) === null || _c === void 0 ? void 0 : _c.insertedCount) !== null && _d !== void 0 ? _d : 0;
58
- return { written, errors: docs.length - written };
59
- }
60
- }
61
- export async function getDbStats(uri) {
62
- const client = new MongoClient(uri);
63
- try {
64
- await client.connect();
65
- const db = client.db();
66
- const stats = await db.stats();
67
- const collList = await db.listCollections().toArray();
68
- const collections = [];
69
- for (const col of collList) {
70
- if (col.name.startsWith("system."))
71
- continue;
72
- try {
73
- const count = await db.collection(col.name).estimatedDocumentCount();
74
- const colStats = await db.command({ collStats: col.name });
75
- collections.push({ name: col.name, documents: count, size: colStats.size || 0 });
76
- }
77
- catch (_a) {
78
- collections.push({ name: col.name, documents: 0, size: 0 });
79
- }
80
- }
81
- collections.sort((a, b) => b.documents - a.documents);
82
- return { database: stats.db, collections, totalSize: stats.dataSize || 0 };
83
- }
84
- finally {
85
- await client.close();
86
- }
87
- }
@@ -1,42 +0,0 @@
1
- import cronParser from "cron-parser";
2
- /**
3
- * Determines whether a cron-scheduled job is due for execution.
4
- * Returns true if the current time has passed the next scheduled run after lastRun.
5
- */
6
- export function isCronDue(expression, lastRun, now = new Date()) {
7
- try {
8
- if (!lastRun)
9
- return true; // Never run before → always due
10
- const lastRunDate = new Date(lastRun);
11
- const interval = cronParser.parseExpression(expression, { currentDate: lastRunDate });
12
- const nextRun = interval.next().toDate();
13
- return now >= nextRun;
14
- }
15
- catch (_a) {
16
- return false; // Invalid cron expression
17
- }
18
- }
19
- /**
20
- * Returns the next scheduled run time for a cron expression.
21
- */
22
- export function getNextRun(expression, after = new Date()) {
23
- try {
24
- const interval = cronParser.parseExpression(expression, { currentDate: after });
25
- return interval.next().toDate();
26
- }
27
- catch (_a) {
28
- return null;
29
- }
30
- }
31
- /**
32
- * Validates a cron expression. Returns null if valid, error message if invalid.
33
- */
34
- export function validateCron(expression) {
35
- try {
36
- cronParser.parseExpression(expression);
37
- return null;
38
- }
39
- catch (e) {
40
- return e.message || "Invalid cron expression";
41
- }
42
- }
package/lib/db/types.js DELETED
@@ -1 +0,0 @@
1
- export {};