@reporting-bi/cli 0.0.10 → 0.0.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/dist/index.js +498 -55
- package/package.json +1 -1
package/dist/index.js
CHANGED
|
@@ -2028,9 +2028,9 @@ var require_stringify = __commonJS({
|
|
|
2028
2028
|
}
|
|
2029
2029
|
};
|
|
2030
2030
|
var isArray = Array.isArray;
|
|
2031
|
-
var
|
|
2031
|
+
var push2 = Array.prototype.push;
|
|
2032
2032
|
var pushToArray = function(arr, valueOrArray) {
|
|
2033
|
-
|
|
2033
|
+
push2.apply(arr, isArray(valueOrArray) ? valueOrArray : [valueOrArray]);
|
|
2034
2034
|
};
|
|
2035
2035
|
var toISO = Date.prototype.toISOString;
|
|
2036
2036
|
var defaultFormat = formats["default"];
|
|
@@ -2650,7 +2650,7 @@ var require_service = __commonJS({
|
|
|
2650
2650
|
this.options.baseUrl = `https://${this.options.baseUrl}`;
|
|
2651
2651
|
}
|
|
2652
2652
|
}
|
|
2653
|
-
createUrl(
|
|
2653
|
+
createUrl(path5 = "", params) {
|
|
2654
2654
|
if (this.options.xdebugSession) {
|
|
2655
2655
|
params = params || {};
|
|
2656
2656
|
params.XDEBUG_SESSION = this.options.xdebugSession === true ? "1" : this.options.xdebugSession.toString();
|
|
@@ -2660,8 +2660,8 @@ var require_service = __commonJS({
|
|
|
2660
2660
|
}
|
|
2661
2661
|
const queryString = params ? "?" + serializeRequestParams(params) : "";
|
|
2662
2662
|
const a = params && params.accounting ? params.accounting.toString() : this.a ? this.a.toString() : null;
|
|
2663
|
-
|
|
2664
|
-
const parts = [this.options.baseUrl, a, this.options.name,
|
|
2663
|
+
path5 = path5.replace(/^\//, "");
|
|
2664
|
+
const parts = [this.options.baseUrl, a, this.options.name, path5];
|
|
2665
2665
|
return parts.filter((p) => p != null && p !== "").join("/") + queryString;
|
|
2666
2666
|
}
|
|
2667
2667
|
async createHeaders(headers) {
|
|
@@ -2702,28 +2702,28 @@ var require_service = __commonJS({
|
|
|
2702
2702
|
return await response.text();
|
|
2703
2703
|
}
|
|
2704
2704
|
}
|
|
2705
|
-
async getRaw(
|
|
2706
|
-
return fetch(this.createUrl(
|
|
2705
|
+
async getRaw(path5, params, options) {
|
|
2706
|
+
return fetch(this.createUrl(path5, params), {
|
|
2707
2707
|
headers: await this.createHeaders(options?.headers),
|
|
2708
2708
|
credentials: this.credentials
|
|
2709
2709
|
});
|
|
2710
2710
|
}
|
|
2711
|
-
async get(
|
|
2711
|
+
async get(path5, params, options) {
|
|
2712
2712
|
if (options?.cache) {
|
|
2713
|
-
const cachedData = this.cache.get({ path:
|
|
2713
|
+
const cachedData = this.cache.get({ path: path5, params });
|
|
2714
2714
|
if (cachedData)
|
|
2715
2715
|
return cachedData;
|
|
2716
2716
|
}
|
|
2717
|
-
const response = await this.getRaw(
|
|
2717
|
+
const response = await this.getRaw(path5, params, options);
|
|
2718
2718
|
const responseData = await this.handleResponse(response, options?.parseResponse);
|
|
2719
2719
|
if (options?.cache && responseData) {
|
|
2720
|
-
this.cache.set({ path:
|
|
2720
|
+
this.cache.set({ path: path5, params }, responseData);
|
|
2721
2721
|
}
|
|
2722
2722
|
return responseData;
|
|
2723
2723
|
}
|
|
2724
|
-
async postRaw(
|
|
2724
|
+
async postRaw(path5, data, params, options) {
|
|
2725
2725
|
const json = isObject(data) || Array.isArray(data);
|
|
2726
|
-
return fetch(this.createUrl(
|
|
2726
|
+
return fetch(this.createUrl(path5, params), {
|
|
2727
2727
|
method: "POST",
|
|
2728
2728
|
body: json ? JSON.stringify(data) : data,
|
|
2729
2729
|
headers: await this.createHeaders({
|
|
@@ -2733,24 +2733,24 @@ var require_service = __commonJS({
|
|
|
2733
2733
|
credentials: this.credentials
|
|
2734
2734
|
});
|
|
2735
2735
|
}
|
|
2736
|
-
async post(
|
|
2737
|
-
const response = await this.postRaw(
|
|
2736
|
+
async post(path5, data, params, options) {
|
|
2737
|
+
const response = await this.postRaw(path5, data, params, options);
|
|
2738
2738
|
return this.handleResponse(response);
|
|
2739
2739
|
}
|
|
2740
|
-
async deleteRaw(
|
|
2741
|
-
return fetch(this.createUrl(
|
|
2740
|
+
async deleteRaw(path5, params, options) {
|
|
2741
|
+
return fetch(this.createUrl(path5, params), {
|
|
2742
2742
|
method: "DELETE",
|
|
2743
2743
|
headers: await this.createHeaders(options?.headers),
|
|
2744
2744
|
credentials: this.credentials
|
|
2745
2745
|
});
|
|
2746
2746
|
}
|
|
2747
|
-
async delete(
|
|
2748
|
-
const response = await this.deleteRaw(
|
|
2747
|
+
async delete(path5, params, options) {
|
|
2748
|
+
const response = await this.deleteRaw(path5, params, options);
|
|
2749
2749
|
return this.handleResponse(response);
|
|
2750
2750
|
}
|
|
2751
|
-
async putRaw(
|
|
2751
|
+
async putRaw(path5, data, params, options) {
|
|
2752
2752
|
const json = isObject(data) || Array.isArray(data);
|
|
2753
|
-
return fetch(this.createUrl(
|
|
2753
|
+
return fetch(this.createUrl(path5, params), {
|
|
2754
2754
|
method: "PUT",
|
|
2755
2755
|
body: json ? JSON.stringify(data) : data,
|
|
2756
2756
|
headers: await this.createHeaders({
|
|
@@ -2760,8 +2760,8 @@ var require_service = __commonJS({
|
|
|
2760
2760
|
credentials: this.credentials
|
|
2761
2761
|
});
|
|
2762
2762
|
}
|
|
2763
|
-
async put(
|
|
2764
|
-
const response = await this.putRaw(
|
|
2763
|
+
async put(path5, data, params, options) {
|
|
2764
|
+
const response = await this.putRaw(path5, data, params, options);
|
|
2765
2765
|
return this.handleResponse(response);
|
|
2766
2766
|
}
|
|
2767
2767
|
};
|
|
@@ -3084,14 +3084,16 @@ async function ensureValidToken(conn) {
|
|
|
3084
3084
|
console.error("Token refresh failed:", err instanceof Error ? err.message : err);
|
|
3085
3085
|
}
|
|
3086
3086
|
}
|
|
3087
|
-
async function createService(name,
|
|
3087
|
+
async function createService(name, options) {
|
|
3088
3088
|
const conn = getActiveConnection();
|
|
3089
3089
|
if (!conn) {
|
|
3090
3090
|
console.error('Not logged in. Run "rpbi login" first.');
|
|
3091
3091
|
process.exit(1);
|
|
3092
3092
|
}
|
|
3093
|
+
const accountingOverride = typeof options === "number" ? options : void 0;
|
|
3094
|
+
const accountingOptional = typeof options === "object" && options.accountingOptional;
|
|
3093
3095
|
const accounting2 = accountingOverride ?? conn.tokens.accounting;
|
|
3094
|
-
if (!accounting2) {
|
|
3096
|
+
if (!accounting2 && !accountingOptional) {
|
|
3095
3097
|
console.error('No accounting set. Run "rpbi use <domain> --accounting <id>" first.');
|
|
3096
3098
|
process.exit(1);
|
|
3097
3099
|
}
|
|
@@ -3102,9 +3104,11 @@ async function createService(name, accountingOverride) {
|
|
|
3102
3104
|
throwExceptions: true
|
|
3103
3105
|
});
|
|
3104
3106
|
service.tokenProvider = () => conn.tokens.access_token;
|
|
3105
|
-
|
|
3106
|
-
|
|
3107
|
-
|
|
3107
|
+
if (accounting2) {
|
|
3108
|
+
const ap = new import_rp_client.AccountingProvider();
|
|
3109
|
+
ap.a = accounting2;
|
|
3110
|
+
service.accountingProvider = ap;
|
|
3111
|
+
}
|
|
3108
3112
|
return service;
|
|
3109
3113
|
}
|
|
3110
3114
|
function getFields(flags, defaults) {
|
|
@@ -3221,7 +3225,7 @@ async function accounting(args) {
|
|
|
3221
3225
|
}
|
|
3222
3226
|
}
|
|
3223
3227
|
async function list(flags) {
|
|
3224
|
-
const service = await createService("accounting");
|
|
3228
|
+
const service = await createService("accounting", { accountingOptional: true });
|
|
3225
3229
|
const accountings = await service.get("accountings", getListParams(flags, ["accounting", "company"]));
|
|
3226
3230
|
output(accountings || [], flags);
|
|
3227
3231
|
}
|
|
@@ -3286,8 +3290,8 @@ var callback_default = `<!DOCTYPE html>
|
|
|
3286
3290
|
`;
|
|
3287
3291
|
|
|
3288
3292
|
// src/commands/login.ts
|
|
3289
|
-
function renderCallback(
|
|
3290
|
-
return callback_default.replace("{{status}}",
|
|
3293
|
+
function renderCallback(status3, title, message) {
|
|
3294
|
+
return callback_default.replace("{{status}}", status3).replace("{{title}}", title).replace("{{message}}", message);
|
|
3291
3295
|
}
|
|
3292
3296
|
function openBrowser(url) {
|
|
3293
3297
|
const platform = process.platform;
|
|
@@ -3331,12 +3335,12 @@ async function login(args) {
|
|
|
3331
3335
|
}
|
|
3332
3336
|
}
|
|
3333
3337
|
function getAuthorizationCode(domain) {
|
|
3334
|
-
return new Promise((
|
|
3338
|
+
return new Promise((resolve3) => {
|
|
3335
3339
|
let timeout;
|
|
3336
3340
|
const done = (result) => {
|
|
3337
3341
|
clearTimeout(timeout);
|
|
3338
3342
|
server.close();
|
|
3339
|
-
|
|
3343
|
+
resolve3(result);
|
|
3340
3344
|
};
|
|
3341
3345
|
const server = http.createServer((req, res) => {
|
|
3342
3346
|
const url = new URL(req.url || "", "http://localhost");
|
|
@@ -3496,11 +3500,11 @@ async function readContent(flags) {
|
|
|
3496
3500
|
return flags.content;
|
|
3497
3501
|
}
|
|
3498
3502
|
if (!process.stdin.isTTY) {
|
|
3499
|
-
return new Promise((
|
|
3503
|
+
return new Promise((resolve3) => {
|
|
3500
3504
|
let data = "";
|
|
3501
3505
|
process.stdin.setEncoding("utf-8");
|
|
3502
3506
|
process.stdin.on("data", (chunk) => data += chunk);
|
|
3503
|
-
process.stdin.on("end", () =>
|
|
3507
|
+
process.stdin.on("end", () => resolve3(data));
|
|
3504
3508
|
});
|
|
3505
3509
|
}
|
|
3506
3510
|
console.error("No content provided. Use --file, --content, or pipe to stdin.");
|
|
@@ -3564,7 +3568,433 @@ async function wiki(args) {
|
|
|
3564
3568
|
}
|
|
3565
3569
|
|
|
3566
3570
|
// src/commands/etl.ts
|
|
3571
|
+
var fs5 = __toESM(require("node:fs"));
|
|
3572
|
+
|
|
3573
|
+
// src/commands/etl-sync.ts
|
|
3574
|
+
var fs4 = __toESM(require("node:fs"));
|
|
3575
|
+
var path3 = __toESM(require("node:path"));
|
|
3576
|
+
|
|
3577
|
+
// src/manifest.ts
|
|
3567
3578
|
var fs3 = __toESM(require("node:fs"));
|
|
3579
|
+
var path2 = __toESM(require("node:path"));
|
|
3580
|
+
var crypto = __toESM(require("node:crypto"));
|
|
3581
|
+
var RPBI_DIR = ".rpbi";
|
|
3582
|
+
var MANIFEST_FILE = "manifest.json";
|
|
3583
|
+
function computeHash(content2) {
|
|
3584
|
+
return crypto.createHash("sha256").update(content2, "utf-8").digest("hex");
|
|
3585
|
+
}
|
|
3586
|
+
function getManifestPath(dir) {
|
|
3587
|
+
return path2.join(dir, RPBI_DIR, MANIFEST_FILE);
|
|
3588
|
+
}
|
|
3589
|
+
function readManifest(dir) {
|
|
3590
|
+
const filePath = getManifestPath(dir);
|
|
3591
|
+
try {
|
|
3592
|
+
const data = fs3.readFileSync(filePath, "utf-8");
|
|
3593
|
+
return JSON.parse(data);
|
|
3594
|
+
} catch {
|
|
3595
|
+
return null;
|
|
3596
|
+
}
|
|
3597
|
+
}
|
|
3598
|
+
function writeManifest(dir, manifest) {
|
|
3599
|
+
const rpbiDir = path2.join(dir, RPBI_DIR);
|
|
3600
|
+
fs3.mkdirSync(rpbiDir, { recursive: true });
|
|
3601
|
+
fs3.writeFileSync(
|
|
3602
|
+
path2.join(rpbiDir, MANIFEST_FILE),
|
|
3603
|
+
JSON.stringify(manifest, null, 2)
|
|
3604
|
+
);
|
|
3605
|
+
}
|
|
3606
|
+
function findEtlRoot(startDir) {
|
|
3607
|
+
let dir = path2.resolve(startDir);
|
|
3608
|
+
while (true) {
|
|
3609
|
+
if (fs3.existsSync(getManifestPath(dir))) return dir;
|
|
3610
|
+
const parent = path2.dirname(dir);
|
|
3611
|
+
if (parent === dir) return null;
|
|
3612
|
+
dir = parent;
|
|
3613
|
+
}
|
|
3614
|
+
}
|
|
3615
|
+
|
|
3616
|
+
// src/commands/etl-sync.ts
|
|
3617
|
+
var PARAMETER_TYPES = ["IA", "IM", "O", "D"];
|
|
3618
|
+
var PARAMETER_EXCLUDE_FIELDS = ["accounting", "id_thread", "idz", "validate_flag", "date_created"];
|
|
3619
|
+
var THREAD_EXCLUDE_FIELDS = ["command_txt", "command_preview", "accounting", "id_thread", "type", "idz", "validate_flag", "date_created"];
|
|
3620
|
+
var CONTEXT_SCHEMA_DIRS = {
|
|
3621
|
+
"schema/db_data/tables": { db: "db_data", objectType: "tables" },
|
|
3622
|
+
"schema/db_data/procedures": { db: "db_data", objectType: "procedures" },
|
|
3623
|
+
"schema/db_data/functions": { db: "db_data", objectType: "functions" },
|
|
3624
|
+
"schema/db_master/views": { db: "db_master", objectType: "views" },
|
|
3625
|
+
"schema/db_master/procedures": { db: "db_master", objectType: "procedures" },
|
|
3626
|
+
"schema/db_master/functions": { db: "db_master", objectType: "functions" },
|
|
3627
|
+
"schema/db_super/views": { db: "db_super", objectType: "views" },
|
|
3628
|
+
"schema/db_super/procedures": { db: "db_super", objectType: "procedures" },
|
|
3629
|
+
"schema/db_super/functions": { db: "db_super", objectType: "functions" }
|
|
3630
|
+
};
|
|
3631
|
+
function optimizeForLlm(data) {
|
|
3632
|
+
if (data === null || data === void 0) return void 0;
|
|
3633
|
+
if (Array.isArray(data)) {
|
|
3634
|
+
const result = data.map((item) => optimizeForLlm(item)).filter((item) => item !== void 0);
|
|
3635
|
+
return result.length > 0 ? result : void 0;
|
|
3636
|
+
}
|
|
3637
|
+
if (typeof data === "object") {
|
|
3638
|
+
const result = {};
|
|
3639
|
+
let hasKeys = false;
|
|
3640
|
+
for (const [key, value] of Object.entries(data)) {
|
|
3641
|
+
const optimized = optimizeForLlm(value);
|
|
3642
|
+
if (optimized !== void 0) {
|
|
3643
|
+
result[key] = optimized;
|
|
3644
|
+
hasKeys = true;
|
|
3645
|
+
}
|
|
3646
|
+
}
|
|
3647
|
+
return hasKeys ? result : void 0;
|
|
3648
|
+
}
|
|
3649
|
+
return data;
|
|
3650
|
+
}
|
|
3651
|
+
function getFilePath(description, extension = ".sql") {
|
|
3652
|
+
const parts = description.split("/");
|
|
3653
|
+
const name = parts.pop();
|
|
3654
|
+
let fileName;
|
|
3655
|
+
if (!name.includes(".")) {
|
|
3656
|
+
fileName = name + extension;
|
|
3657
|
+
} else if (name.endsWith(".sql") && extension !== ".sql") {
|
|
3658
|
+
fileName = name.slice(0, -4) + extension;
|
|
3659
|
+
} else if (extension !== ".sql") {
|
|
3660
|
+
const dotIndex = name.lastIndexOf(".");
|
|
3661
|
+
fileName = name.slice(0, dotIndex) + extension;
|
|
3662
|
+
} else {
|
|
3663
|
+
fileName = name;
|
|
3664
|
+
}
|
|
3665
|
+
return [...parts, fileName];
|
|
3666
|
+
}
|
|
3667
|
+
function threadToRelPath(thread) {
|
|
3668
|
+
const segments = getFilePath(thread.description || "");
|
|
3669
|
+
return [thread.type, ...segments].join("/");
|
|
3670
|
+
}
|
|
3671
|
+
function threadToParamPath(thread) {
|
|
3672
|
+
const segments = getFilePath(thread.description || "", ".yaml");
|
|
3673
|
+
return [".parameters", thread.type, ...segments].join("/");
|
|
3674
|
+
}
|
|
3675
|
+
function threadToMetaPath(thread) {
|
|
3676
|
+
const segments = getFilePath(thread.description || "", ".yaml");
|
|
3677
|
+
return [".threads", thread.type, ...segments].join("/");
|
|
3678
|
+
}
|
|
3679
|
+
function writeFileSync4(filePath, content2) {
|
|
3680
|
+
fs4.mkdirSync(path3.dirname(filePath), { recursive: true });
|
|
3681
|
+
fs4.writeFileSync(filePath, content2, "utf-8");
|
|
3682
|
+
}
|
|
3683
|
+
function stripFields(obj, excludeFields) {
|
|
3684
|
+
const result = {};
|
|
3685
|
+
for (const [key, value] of Object.entries(obj)) {
|
|
3686
|
+
if (!excludeFields.includes(key)) {
|
|
3687
|
+
result[key] = value;
|
|
3688
|
+
}
|
|
3689
|
+
}
|
|
3690
|
+
return result;
|
|
3691
|
+
}
|
|
3692
|
+
function toYaml(data, indent = 0) {
|
|
3693
|
+
if (data === null || data === void 0) return "null";
|
|
3694
|
+
if (typeof data === "boolean") return data ? "true" : "false";
|
|
3695
|
+
if (typeof data === "number") return String(data);
|
|
3696
|
+
if (typeof data === "string") {
|
|
3697
|
+
if (data.includes("\n")) {
|
|
3698
|
+
const prefix = " ".repeat(indent);
|
|
3699
|
+
const lines = data.split("\n").map((l) => prefix + " " + l).join("\n");
|
|
3700
|
+
return "|\n" + lines;
|
|
3701
|
+
}
|
|
3702
|
+
if (data === "" || /[:{}\[\],&*?|>!'"%@`#]/.test(data) || /^\s|\s$/.test(data)) {
|
|
3703
|
+
return JSON.stringify(data);
|
|
3704
|
+
}
|
|
3705
|
+
return data;
|
|
3706
|
+
}
|
|
3707
|
+
if (Array.isArray(data)) {
|
|
3708
|
+
if (data.length === 0) return "[]";
|
|
3709
|
+
const prefix = " ".repeat(indent);
|
|
3710
|
+
return data.map((item) => {
|
|
3711
|
+
if (typeof item === "object" && item !== null && !Array.isArray(item)) {
|
|
3712
|
+
const entries = Object.entries(item);
|
|
3713
|
+
if (entries.length === 0) return `${prefix}- {}`;
|
|
3714
|
+
const [firstKey, firstVal] = entries[0];
|
|
3715
|
+
const firstLine = `${prefix}- ${firstKey}: ${toYaml(firstVal, indent + 2)}`;
|
|
3716
|
+
const rest = entries.slice(1).map(
|
|
3717
|
+
([k, v]) => `${prefix} ${k}: ${toYaml(v, indent + 2)}`
|
|
3718
|
+
);
|
|
3719
|
+
return [firstLine, ...rest].join("\n");
|
|
3720
|
+
}
|
|
3721
|
+
return `${prefix}- ${toYaml(item, indent + 2)}`;
|
|
3722
|
+
}).join("\n");
|
|
3723
|
+
}
|
|
3724
|
+
if (typeof data === "object") {
|
|
3725
|
+
const entries = Object.entries(data);
|
|
3726
|
+
if (entries.length === 0) return "{}";
|
|
3727
|
+
const prefix = " ".repeat(indent);
|
|
3728
|
+
return entries.map(
|
|
3729
|
+
([k, v]) => `${prefix}${k}: ${toYaml(v, indent)}`
|
|
3730
|
+
).join("\n");
|
|
3731
|
+
}
|
|
3732
|
+
return String(data);
|
|
3733
|
+
}
|
|
3734
|
+
async function pull(flags) {
|
|
3735
|
+
const dir = path3.resolve(flags.dir || ".");
|
|
3736
|
+
const conn = getActiveConnection();
|
|
3737
|
+
if (!conn) {
|
|
3738
|
+
console.error('Not logged in. Run "rpbi login" first.');
|
|
3739
|
+
process.exit(1);
|
|
3740
|
+
}
|
|
3741
|
+
const etlService = await createService("etl");
|
|
3742
|
+
const defService = await createService("definition");
|
|
3743
|
+
console.log(`Pulling ETL threads to ${dir}...`);
|
|
3744
|
+
const threads = await etlService.get("threads", {
|
|
3745
|
+
fields: ["id_thread", "description", "type", "enabled", "ord_num", "module"],
|
|
3746
|
+
order: [["type", "asc"], ["description", "asc"]]
|
|
3747
|
+
}) || [];
|
|
3748
|
+
console.log(`Found ${threads.length} threads`);
|
|
3749
|
+
const manifest = {
|
|
3750
|
+
domain: conn.domain,
|
|
3751
|
+
accounting: conn.tokens.accounting,
|
|
3752
|
+
pulledAt: (/* @__PURE__ */ new Date()).toISOString(),
|
|
3753
|
+
threads: {}
|
|
3754
|
+
};
|
|
3755
|
+
let pulled = 0;
|
|
3756
|
+
for (const thread of threads) {
|
|
3757
|
+
if (!thread.id_thread || !thread.type || !thread.description) continue;
|
|
3758
|
+
const relPath = threadToRelPath(thread);
|
|
3759
|
+
const absPath = path3.join(dir, relPath);
|
|
3760
|
+
try {
|
|
3761
|
+
const content2 = await etlService.get(
|
|
3762
|
+
`threadContent/${thread.id_thread}`,
|
|
3763
|
+
void 0,
|
|
3764
|
+
{ parseResponse: false }
|
|
3765
|
+
) || "";
|
|
3766
|
+
writeFileSync4(absPath, content2);
|
|
3767
|
+
manifest.threads[relPath] = {
|
|
3768
|
+
id_thread: thread.id_thread,
|
|
3769
|
+
hash: computeHash(content2),
|
|
3770
|
+
type: thread.type
|
|
3771
|
+
};
|
|
3772
|
+
pulled++;
|
|
3773
|
+
if (pulled % 50 === 0) console.log(` ${pulled}/${threads.length} threads...`);
|
|
3774
|
+
} catch (err) {
|
|
3775
|
+
console.error(` Failed to pull ${relPath}: ${err instanceof Error ? err.message : err}`);
|
|
3776
|
+
}
|
|
3777
|
+
}
|
|
3778
|
+
console.log("Pulling parameters...");
|
|
3779
|
+
for (const thread of threads) {
|
|
3780
|
+
if (!thread.id_thread || !PARAMETER_TYPES.includes(thread.type || "")) continue;
|
|
3781
|
+
try {
|
|
3782
|
+
const params = await etlService.get(
|
|
3783
|
+
`threads/${thread.id_thread}/parameters`
|
|
3784
|
+
) || [];
|
|
3785
|
+
if (params.length === 0) continue;
|
|
3786
|
+
const cleaned = params.map((p) => stripFields(p, PARAMETER_EXCLUDE_FIELDS));
|
|
3787
|
+
const optimized = optimizeForLlm(cleaned);
|
|
3788
|
+
if (!optimized || !Array.isArray(optimized) || optimized.length === 0) continue;
|
|
3789
|
+
const paramPath = path3.join(dir, threadToParamPath(thread));
|
|
3790
|
+
writeFileSync4(paramPath, toYaml(optimized) + "\n");
|
|
3791
|
+
} catch {
|
|
3792
|
+
}
|
|
3793
|
+
}
|
|
3794
|
+
console.log("Pulling thread metadata...");
|
|
3795
|
+
for (const thread of threads) {
|
|
3796
|
+
if (!thread.id_thread) continue;
|
|
3797
|
+
try {
|
|
3798
|
+
const full = await etlService.get(`threads/${thread.id_thread}`);
|
|
3799
|
+
if (!full) continue;
|
|
3800
|
+
const cleaned = stripFields(full, THREAD_EXCLUDE_FIELDS);
|
|
3801
|
+
const optimized = optimizeForLlm(cleaned);
|
|
3802
|
+
if (!optimized || Object.keys(optimized).length === 0) continue;
|
|
3803
|
+
const metaPath = path3.join(dir, threadToMetaPath(thread));
|
|
3804
|
+
writeFileSync4(metaPath, toYaml(optimized) + "\n");
|
|
3805
|
+
} catch {
|
|
3806
|
+
}
|
|
3807
|
+
}
|
|
3808
|
+
if (!flags["no-context"]) {
|
|
3809
|
+
await pullContext(dir, etlService, defService);
|
|
3810
|
+
}
|
|
3811
|
+
writeManifest(dir, manifest);
|
|
3812
|
+
console.log(`Done. Pulled ${pulled} threads to ${dir}`);
|
|
3813
|
+
}
|
|
3814
|
+
async function push(flags, files) {
|
|
3815
|
+
const startDir = flags.dir || ".";
|
|
3816
|
+
const root = findEtlRoot(startDir);
|
|
3817
|
+
if (!root) {
|
|
3818
|
+
console.error('No .rpbi/manifest.json found. Run "rpbi etl pull" first.');
|
|
3819
|
+
process.exit(1);
|
|
3820
|
+
}
|
|
3821
|
+
const manifest = readManifest(root);
|
|
3822
|
+
const etlService = await createService("etl");
|
|
3823
|
+
const force = flags.force === "true";
|
|
3824
|
+
let filesToPush;
|
|
3825
|
+
if (files.length > 0) {
|
|
3826
|
+
filesToPush = files.map((f) => path3.relative(root, path3.resolve(f)));
|
|
3827
|
+
} else {
|
|
3828
|
+
filesToPush = detectModifiedFiles(root, manifest);
|
|
3829
|
+
}
|
|
3830
|
+
if (filesToPush.length === 0) {
|
|
3831
|
+
console.log("Nothing to push.");
|
|
3832
|
+
return;
|
|
3833
|
+
}
|
|
3834
|
+
console.log(`Pushing ${filesToPush.length} file(s)...`);
|
|
3835
|
+
for (const relPath of filesToPush) {
|
|
3836
|
+
const absPath = path3.join(root, relPath);
|
|
3837
|
+
if (!fs4.existsSync(absPath)) {
|
|
3838
|
+
console.log(` Skipping ${relPath} (file not found)`);
|
|
3839
|
+
continue;
|
|
3840
|
+
}
|
|
3841
|
+
const localContent = fs4.readFileSync(absPath, "utf-8");
|
|
3842
|
+
const entry = manifest.threads[relPath];
|
|
3843
|
+
if (entry) {
|
|
3844
|
+
if (!force) {
|
|
3845
|
+
try {
|
|
3846
|
+
const serverContent = await etlService.get(
|
|
3847
|
+
`threadContent/${entry.id_thread}`,
|
|
3848
|
+
void 0,
|
|
3849
|
+
{ parseResponse: false }
|
|
3850
|
+
) || "";
|
|
3851
|
+
const serverHash = computeHash(serverContent);
|
|
3852
|
+
if (serverHash !== entry.hash) {
|
|
3853
|
+
console.error(` CONFLICT: ${relPath} was modified on server. Use --force to overwrite.`);
|
|
3854
|
+
continue;
|
|
3855
|
+
}
|
|
3856
|
+
} catch {
|
|
3857
|
+
}
|
|
3858
|
+
}
|
|
3859
|
+
await etlService.post(`threadContent/${entry.id_thread}`, localContent);
|
|
3860
|
+
manifest.threads[relPath].hash = computeHash(localContent);
|
|
3861
|
+
console.log(` Updated: ${relPath}`);
|
|
3862
|
+
} else {
|
|
3863
|
+
const parts = relPath.split("/");
|
|
3864
|
+
const type = parts[0];
|
|
3865
|
+
const description = parts.slice(1).join("/").replace(/\.sql$/, "");
|
|
3866
|
+
const newThread = await etlService.post("threads", {
|
|
3867
|
+
type,
|
|
3868
|
+
description,
|
|
3869
|
+
enabled: 1
|
|
3870
|
+
});
|
|
3871
|
+
if (newThread?.id_thread) {
|
|
3872
|
+
await etlService.post(`threadContent/${newThread.id_thread}`, localContent);
|
|
3873
|
+
manifest.threads[relPath] = {
|
|
3874
|
+
id_thread: newThread.id_thread,
|
|
3875
|
+
hash: computeHash(localContent),
|
|
3876
|
+
type
|
|
3877
|
+
};
|
|
3878
|
+
console.log(` Created: ${relPath} (thread #${newThread.id_thread})`);
|
|
3879
|
+
} else {
|
|
3880
|
+
console.error(` Failed to create thread for ${relPath}`);
|
|
3881
|
+
}
|
|
3882
|
+
}
|
|
3883
|
+
}
|
|
3884
|
+
writeManifest(path3.resolve(root), manifest);
|
|
3885
|
+
console.log("Push complete.");
|
|
3886
|
+
}
|
|
3887
|
+
async function pullContext(dir, etlService, defService) {
|
|
3888
|
+
const contextDir = path3.join(dir, ".context");
|
|
3889
|
+
console.log("Pulling context (schema & modules)...");
|
|
3890
|
+
try {
|
|
3891
|
+
const modules2 = await defService.get("accountingModules");
|
|
3892
|
+
if (modules2) {
|
|
3893
|
+
const optimized = optimizeForLlm(modules2);
|
|
3894
|
+
writeFileSync4(
|
|
3895
|
+
path3.join(contextDir, "modules.json"),
|
|
3896
|
+
JSON.stringify(optimized, null, 2)
|
|
3897
|
+
);
|
|
3898
|
+
}
|
|
3899
|
+
} catch (err) {
|
|
3900
|
+
console.error(` Failed to pull modules: ${err instanceof Error ? err.message : err}`);
|
|
3901
|
+
}
|
|
3902
|
+
for (const [dirPath, { db, objectType }] of Object.entries(CONTEXT_SCHEMA_DIRS)) {
|
|
3903
|
+
try {
|
|
3904
|
+
const items = await etlService.get(`schema/${db}/${objectType}`) || [];
|
|
3905
|
+
for (const item of items) {
|
|
3906
|
+
const name = item?.name;
|
|
3907
|
+
if (!name) continue;
|
|
3908
|
+
const optimized = optimizeForLlm(item);
|
|
3909
|
+
writeFileSync4(
|
|
3910
|
+
path3.join(contextDir, dirPath, `${name}.json`),
|
|
3911
|
+
JSON.stringify(optimized, null, 2)
|
|
3912
|
+
);
|
|
3913
|
+
}
|
|
3914
|
+
} catch {
|
|
3915
|
+
}
|
|
3916
|
+
}
|
|
3917
|
+
}
|
|
3918
|
+
async function context(flags) {
|
|
3919
|
+
const dir = path3.resolve(flags.dir || ".");
|
|
3920
|
+
const etlService = await createService("etl");
|
|
3921
|
+
const defService = await createService("definition");
|
|
3922
|
+
await pullContext(dir, etlService, defService);
|
|
3923
|
+
console.log("Context pull complete.");
|
|
3924
|
+
}
|
|
3925
|
+
function detectModifiedFiles(root, manifest) {
|
|
3926
|
+
const modified = [];
|
|
3927
|
+
for (const [relPath, entry] of Object.entries(manifest.threads)) {
|
|
3928
|
+
const absPath = path3.join(root, relPath);
|
|
3929
|
+
if (!fs4.existsSync(absPath)) {
|
|
3930
|
+
modified.push(relPath);
|
|
3931
|
+
continue;
|
|
3932
|
+
}
|
|
3933
|
+
const content2 = fs4.readFileSync(absPath, "utf-8");
|
|
3934
|
+
if (computeHash(content2) !== entry.hash) {
|
|
3935
|
+
modified.push(relPath);
|
|
3936
|
+
}
|
|
3937
|
+
}
|
|
3938
|
+
return modified;
|
|
3939
|
+
}
|
|
3940
|
+
function detectNewFiles(root, manifest) {
|
|
3941
|
+
const newFiles = [];
|
|
3942
|
+
const entries = fs4.readdirSync(root, { withFileTypes: true });
|
|
3943
|
+
for (const entry of entries) {
|
|
3944
|
+
if (!entry.isDirectory() || entry.name.startsWith(".")) continue;
|
|
3945
|
+
walkDir(path3.join(root, entry.name), root, manifest, newFiles);
|
|
3946
|
+
}
|
|
3947
|
+
return newFiles;
|
|
3948
|
+
}
|
|
3949
|
+
function walkDir(dir, root, manifest, result) {
|
|
3950
|
+
const entries = fs4.readdirSync(dir, { withFileTypes: true });
|
|
3951
|
+
for (const entry of entries) {
|
|
3952
|
+
const full = path3.join(dir, entry.name);
|
|
3953
|
+
if (entry.isDirectory()) {
|
|
3954
|
+
walkDir(full, root, manifest, result);
|
|
3955
|
+
} else if (entry.name.endsWith(".sql")) {
|
|
3956
|
+
const rel = path3.relative(root, full);
|
|
3957
|
+
if (!manifest.threads[rel]) {
|
|
3958
|
+
result.push(rel);
|
|
3959
|
+
}
|
|
3960
|
+
}
|
|
3961
|
+
}
|
|
3962
|
+
}
|
|
3963
|
+
async function status2(flags) {
|
|
3964
|
+
const startDir = flags.dir || ".";
|
|
3965
|
+
const root = findEtlRoot(startDir);
|
|
3966
|
+
if (!root) {
|
|
3967
|
+
console.error('No .rpbi/manifest.json found. Run "rpbi etl pull" first.');
|
|
3968
|
+
process.exit(1);
|
|
3969
|
+
}
|
|
3970
|
+
const manifest = readManifest(root);
|
|
3971
|
+
console.log(`ETL workspace: ${root}`);
|
|
3972
|
+
console.log(`Domain: ${manifest.domain} | Accounting: ${manifest.accounting}`);
|
|
3973
|
+
console.log(`Pulled at: ${manifest.pulledAt}`);
|
|
3974
|
+
console.log("");
|
|
3975
|
+
const modified = detectModifiedFiles(root, manifest);
|
|
3976
|
+
const newFiles = detectNewFiles(root, manifest);
|
|
3977
|
+
const deleted = modified.filter((f) => !fs4.existsSync(path3.join(root, f)));
|
|
3978
|
+
const changed = modified.filter((f) => fs4.existsSync(path3.join(root, f)));
|
|
3979
|
+
if (changed.length === 0 && newFiles.length === 0 && deleted.length === 0) {
|
|
3980
|
+
console.log("No changes.");
|
|
3981
|
+
return;
|
|
3982
|
+
}
|
|
3983
|
+
if (changed.length > 0) {
|
|
3984
|
+
console.log("Modified:");
|
|
3985
|
+
for (const f of changed) console.log(` M ${f}`);
|
|
3986
|
+
}
|
|
3987
|
+
if (newFiles.length > 0) {
|
|
3988
|
+
console.log("New (untracked):");
|
|
3989
|
+
for (const f of newFiles) console.log(` + ${f}`);
|
|
3990
|
+
}
|
|
3991
|
+
if (deleted.length > 0) {
|
|
3992
|
+
console.log("Deleted locally:");
|
|
3993
|
+
for (const f of deleted) console.log(` - ${f}`);
|
|
3994
|
+
}
|
|
3995
|
+
}
|
|
3996
|
+
|
|
3997
|
+
// src/commands/etl.ts
|
|
3568
3998
|
var DEFAULT_FIELDS = ["id_thread", "description", "module", "type", "enabled", "ord_num"];
|
|
3569
3999
|
async function list3(flags) {
|
|
3570
4000
|
const service = await createService("etl");
|
|
@@ -3585,7 +4015,7 @@ async function exec2(id, flags) {
|
|
|
3585
4015
|
const service = await createService("etl");
|
|
3586
4016
|
let body = void 0;
|
|
3587
4017
|
if (flags.file) {
|
|
3588
|
-
body =
|
|
4018
|
+
body = fs5.readFileSync(flags.file, "utf-8");
|
|
3589
4019
|
} else if (flags.sql) {
|
|
3590
4020
|
body = flags.sql;
|
|
3591
4021
|
}
|
|
@@ -3627,8 +4057,16 @@ async function etl(args) {
|
|
|
3627
4057
|
process.exit(1);
|
|
3628
4058
|
}
|
|
3629
4059
|
return search2(rest[0], flags);
|
|
4060
|
+
case "pull":
|
|
4061
|
+
return pull(flags);
|
|
4062
|
+
case "push":
|
|
4063
|
+
return push(flags, rest);
|
|
4064
|
+
case "context":
|
|
4065
|
+
return context(flags);
|
|
4066
|
+
case "status":
|
|
4067
|
+
return status2(flags);
|
|
3630
4068
|
default:
|
|
3631
|
-
console.log("Usage: rpbi etl <list|get|content|exec|search> [options]");
|
|
4069
|
+
console.log("Usage: rpbi etl <list|get|content|exec|search|pull|push|context|status> [options]");
|
|
3632
4070
|
break;
|
|
3633
4071
|
}
|
|
3634
4072
|
}
|
|
@@ -3684,7 +4122,7 @@ async function jobs(args) {
|
|
|
3684
4122
|
|
|
3685
4123
|
// src/commands/analytics.ts
|
|
3686
4124
|
var DEFAULT_CONTEXT_FIELDS = ["id_context", "name", "module", "user_name"];
|
|
3687
|
-
async function
|
|
4125
|
+
async function context2(args) {
|
|
3688
4126
|
const { positional, flags } = parseArgs(args);
|
|
3689
4127
|
const [subcommand, ...rest] = positional;
|
|
3690
4128
|
switch (subcommand) {
|
|
@@ -3974,47 +4412,47 @@ async function helpdesk(args) {
|
|
|
3974
4412
|
}
|
|
3975
4413
|
|
|
3976
4414
|
// src/autoupdate.ts
|
|
3977
|
-
var
|
|
3978
|
-
var
|
|
4415
|
+
var fs6 = __toESM(require("node:fs"));
|
|
4416
|
+
var path4 = __toESM(require("node:path"));
|
|
3979
4417
|
var os2 = __toESM(require("node:os"));
|
|
3980
4418
|
var https = __toESM(require("node:https"));
|
|
3981
4419
|
var import_node_child_process2 = require("node:child_process");
|
|
3982
|
-
var CONFIG_DIR2 =
|
|
3983
|
-
var UPDATE_CHECK_FILE =
|
|
4420
|
+
var CONFIG_DIR2 = path4.join(os2.homedir(), ".config", "reporting-bi");
|
|
4421
|
+
var UPDATE_CHECK_FILE = path4.join(CONFIG_DIR2, "update-check.json");
|
|
3984
4422
|
var CHECK_INTERVAL = 60 * 60 * 1e3;
|
|
3985
4423
|
var PACKAGE_NAME = "@reporting-bi/cli";
|
|
3986
4424
|
function loadUpdateCheck() {
|
|
3987
4425
|
try {
|
|
3988
|
-
return JSON.parse(
|
|
4426
|
+
return JSON.parse(fs6.readFileSync(UPDATE_CHECK_FILE, "utf-8"));
|
|
3989
4427
|
} catch {
|
|
3990
4428
|
return { lastCheck: 0 };
|
|
3991
4429
|
}
|
|
3992
4430
|
}
|
|
3993
4431
|
function saveUpdateCheck(data) {
|
|
3994
|
-
|
|
3995
|
-
|
|
4432
|
+
fs6.mkdirSync(CONFIG_DIR2, { recursive: true });
|
|
4433
|
+
fs6.writeFileSync(UPDATE_CHECK_FILE, JSON.stringify(data, null, 2));
|
|
3996
4434
|
}
|
|
3997
4435
|
function fetchLatestVersion() {
|
|
3998
|
-
return new Promise((
|
|
4436
|
+
return new Promise((resolve3) => {
|
|
3999
4437
|
const req = https.get(`https://registry.npmjs.org/${PACKAGE_NAME}/latest`, { timeout: 5e3 }, (res) => {
|
|
4000
4438
|
if (res.statusCode !== 200) {
|
|
4001
4439
|
res.resume();
|
|
4002
|
-
return
|
|
4440
|
+
return resolve3(null);
|
|
4003
4441
|
}
|
|
4004
4442
|
let body = "";
|
|
4005
4443
|
res.on("data", (chunk) => body += chunk);
|
|
4006
4444
|
res.on("end", () => {
|
|
4007
4445
|
try {
|
|
4008
|
-
|
|
4446
|
+
resolve3(JSON.parse(body).version ?? null);
|
|
4009
4447
|
} catch {
|
|
4010
|
-
|
|
4448
|
+
resolve3(null);
|
|
4011
4449
|
}
|
|
4012
4450
|
});
|
|
4013
4451
|
});
|
|
4014
|
-
req.on("error", () =>
|
|
4452
|
+
req.on("error", () => resolve3(null));
|
|
4015
4453
|
req.on("timeout", () => {
|
|
4016
4454
|
req.destroy();
|
|
4017
|
-
|
|
4455
|
+
resolve3(null);
|
|
4018
4456
|
});
|
|
4019
4457
|
});
|
|
4020
4458
|
}
|
|
@@ -4030,6 +4468,7 @@ function isNewer(remote, local) {
|
|
|
4030
4468
|
return false;
|
|
4031
4469
|
}
|
|
4032
4470
|
async function checkForUpdate() {
|
|
4471
|
+
if (process.env.RPBI_EMBEDDED) return;
|
|
4033
4472
|
try {
|
|
4034
4473
|
const data = loadUpdateCheck();
|
|
4035
4474
|
const now = Date.now();
|
|
@@ -4037,8 +4476,8 @@ async function checkForUpdate() {
|
|
|
4037
4476
|
saveUpdateCheck({ lastCheck: now });
|
|
4038
4477
|
const latest = await fetchLatestVersion();
|
|
4039
4478
|
if (!latest) return;
|
|
4040
|
-
if (!isNewer(latest, "0.0.
|
|
4041
|
-
console.log(`\x1B[33mUpdating ${PACKAGE_NAME} ${"0.0.
|
|
4479
|
+
if (!isNewer(latest, "0.0.12")) return;
|
|
4480
|
+
console.log(`\x1B[33mUpdating ${PACKAGE_NAME} ${"0.0.12"} \u2192 ${latest}...\x1B[0m`);
|
|
4042
4481
|
(0, import_node_child_process2.execSync)(`npm install -g ${PACKAGE_NAME}@${latest}`, { stdio: "inherit" });
|
|
4043
4482
|
console.log(`\x1B[32mUpdated to ${latest}.\x1B[0m`);
|
|
4044
4483
|
} catch {
|
|
@@ -4164,8 +4603,12 @@ Examples:
|
|
|
4164
4603
|
|
|
4165
4604
|
// src/index.ts
|
|
4166
4605
|
(async () => {
|
|
4167
|
-
await checkForUpdate();
|
|
4168
4606
|
const [command, ...args] = process.argv.slice(2);
|
|
4607
|
+
if (command === "--version" || command === "-v") {
|
|
4608
|
+
console.log("0.0.12");
|
|
4609
|
+
return;
|
|
4610
|
+
}
|
|
4611
|
+
await checkForUpdate();
|
|
4169
4612
|
switch (command) {
|
|
4170
4613
|
case "login":
|
|
4171
4614
|
login(args);
|
|
@@ -4189,7 +4632,7 @@ Examples:
|
|
|
4189
4632
|
jobs(args);
|
|
4190
4633
|
break;
|
|
4191
4634
|
case "context":
|
|
4192
|
-
|
|
4635
|
+
context2(args);
|
|
4193
4636
|
break;
|
|
4194
4637
|
case "report":
|
|
4195
4638
|
report(args);
|