spice-js 2.6.39 → 2.6.40

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.
@@ -3,12 +3,14 @@
3
3
  exports.__esModule = true;
4
4
  exports.default = void 0;
5
5
 
6
- var _flat = _interopRequireDefault(require("flat"));
7
-
8
6
  var _lodash = _interopRequireDefault(require("lodash"));
9
7
 
10
8
  function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; }
11
9
 
10
+ function _getRequireWildcardCache() { if (typeof WeakMap !== "function") return null; var cache = new WeakMap(); _getRequireWildcardCache = function _getRequireWildcardCache() { return cache; }; return cache; }
11
+
12
+ function _interopRequireWildcard(obj) { if (obj && obj.__esModule) { return obj; } if (obj === null || typeof obj !== "object" && typeof obj !== "function") { return { default: obj }; } var cache = _getRequireWildcardCache(); if (cache && cache.has(obj)) { return cache.get(obj); } var newObj = {}; var hasPropertyDescriptor = Object.defineProperty && Object.getOwnPropertyDescriptor; for (var key in obj) { if (Object.prototype.hasOwnProperty.call(obj, key)) { var desc = hasPropertyDescriptor ? Object.getOwnPropertyDescriptor(obj, key) : null; if (desc && (desc.get || desc.set)) { Object.defineProperty(newObj, key, desc); } else { newObj[key] = obj[key]; } } } newObj.default = obj; if (cache) { cache.set(obj, newObj); } return newObj; }
13
+
12
14
  function asyncGeneratorStep(gen, resolve, reject, _next, _throw, key, arg) { try { var info = gen[key](arg); var value = info.value; } catch (error) { reject(error); return; } if (info.done) { resolve(value); } else { Promise.resolve(value).then(_next, _throw); } }
13
15
 
14
16
  function _asyncToGenerator(fn) { return function () { var self = this, args = arguments; return new Promise(function (resolve, reject) { var gen = fn.apply(self, args); function _next(value) { asyncGeneratorStep(gen, resolve, reject, _next, _throw, "next", value); } function _throw(err) { asyncGeneratorStep(gen, resolve, reject, _next, _throw, "throw", err); } _next(undefined); }); }; }
@@ -75,8 +77,12 @@ class RestHelper {
75
77
  var content;
76
78
 
77
79
  if (download_type == "csv") {
80
+ var {
81
+ flatten
82
+ } = yield Promise.resolve().then(() => _interopRequireWildcard(require("flat")));
83
+
78
84
  var items = _lodash.default.map(ctx.data, item => {
79
- return (0, _flat.default)(item);
85
+ return flatten(item);
80
86
  });
81
87
 
82
88
  var fields = _lodash.default.union(_lodash.default.keys(_lodash.default.first(items)), _lodash.default.keys(_lodash.default.last(items)), _lodash.default.keys(_lodash.default.nth(items.length / 2)));
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "spice-js",
3
- "version": "2.6.39",
3
+ "version": "2.6.40",
4
4
  "description": "spice",
5
5
  "main": "build/index.js",
6
6
  "repository": {
@@ -14,28 +14,28 @@
14
14
  "author": "Chad Fraser <chad@sonover.com> (http://sonover.com)",
15
15
  "license": "ISC",
16
16
  "dependencies": {
17
- "agenda": "^3.1.0",
17
+ "agenda": "^5.0.0",
18
18
  "co": "^4.6.0",
19
- "dotenv": "^8.2.0",
20
- "flat": "^5.0.0",
21
- "fs-extra": "^8.1.0",
19
+ "dotenv": "^16.4.5",
20
+ "flat": "^6.0.1",
21
+ "fs-extra": "^11.2.0",
22
22
  "json2csv": "^4.5.4",
23
- "juice": "^6.0.0",
23
+ "juice": "^11.0.0",
24
24
  "kcors": "^2.2.2",
25
- "koa-body": "^4.1.1",
26
- "koa-convert": "^1.2.0",
27
- "koa-qs": "^2.0.0",
28
- "koa-socket-2": "^1.2.0",
25
+ "koa-body": "^6.0.1",
26
+ "koa-convert": "^2.0.0",
27
+ "koa-qs": "^3.0.0",
28
+ "koa-socket-2": "^2.0.0",
29
29
  "koa-static": "^5.0.0",
30
30
  "koa-validation": "^0.1.9",
31
31
  "koa-views": "^6.2.3",
32
- "koa2-swagger-ui": "^5.5.1",
32
+ "koa2-swagger-ui": "^5.10.0",
33
33
  "lodash": "^4.17.15",
34
34
  "node-cache": "^5.1.2",
35
35
  "nunjucks": "^3.2.0",
36
- "open": "7.0.0",
36
+ "open": "10.1.0",
37
37
  "parse-comments": "^1.0.0",
38
- "regenerator-runtime": "^0.13.7",
38
+ "regenerator-runtime": "^0.14.1",
39
39
  "socket.io-client": "^4.0.1",
40
40
  "sonover-date": "^1.0.1",
41
41
  "uuid": "^3.3.3",
@@ -0,0 +1,244 @@
1
+ import _ from "lodash";
2
+ const crypto = require("crypto");
3
+ const path = require("path");
4
+ import fs, { promises, WriteStream } from "fs";
5
+ import { pipeline } from "stream/promises";
6
+ import { spawn } from "child_process";
7
+
8
+ export default class AI {
9
+ constructor(args = {}) {
10
+ this.provider = args.provider || spice.config.ai.default_driver;
11
+ console.log(this.provider, args.provider, spice.config.ai.default_driver);
12
+ this.options = _.merge(
13
+ spice.config.ai.drivers[this.provider],
14
+ args.options
15
+ );
16
+ //console.log(this.provider_name, spice.config.ai.providers)
17
+ this.Provider = spice.config.ai.providers[this.provider];
18
+ }
19
+
20
+ createUniqueDirectory() {
21
+ const timestamp = new Date().toISOString().replace(/[:.-]/g, "");
22
+ const randomString = crypto.randomBytes(4).toString("hex");
23
+ const uniqueDirName = `job_${timestamp}_${randomString}`;
24
+ const project_root = path.resolve(spice.root_path, "..");
25
+ const storageDir = path.join(project_root, "storage/jobs");
26
+ const uniqueDir = path.join(storageDir, uniqueDirName);
27
+
28
+ if (!fs.existsSync(storageDir)) {
29
+ fs.mkdirSync(storageDir, { recursive: true });
30
+ }
31
+ fs.mkdirSync(uniqueDir);
32
+
33
+ return uniqueDir;
34
+ }
35
+
36
+ async extractOrStoreFile(fileStream, outputDir, supportedFileTypes) {
37
+ // Ensure the output directory exists
38
+ if (!fs.existsSync(outputDir)) {
39
+ fs.mkdirSync(outputDir, { recursive: true });
40
+ }
41
+
42
+ // Create a temporary file to store the input
43
+ const tempFilePath = path.join(outputDir, "temp_input");
44
+ await pipeline(fileStream, fs.createWriteStream(tempFilePath));
45
+
46
+ try {
47
+ // Check file type
48
+ const fileType = await this.getFileType(tempFilePath);
49
+
50
+ if (_.includes(supportedFileTypes, fileType)) {
51
+ // If it's a JPEG or PNG, just move it to the output directory
52
+ const timestamp = new Date().toISOString().replace(/[:.-]/g, "");
53
+ const extension = fileType === "jpeg" ? "jpg" : fileType;
54
+ const outputPath = path.join(outputDir, `${timestamp}.${extension}`);
55
+ fs.renameSync(tempFilePath, outputPath);
56
+ return [
57
+ {
58
+ type: fileType,
59
+ media_type: this.getMimeType(fileType),
60
+ path: outputPath,
61
+ },
62
+ ];
63
+ } else {
64
+ // If it's a PDF, extract pages
65
+ if (fileType === "pdf") {
66
+ const outputPrefix = path.join(outputDir, "page");
67
+
68
+ // Use pdftoppm (part of poppler-utils) to convert PDF to images
69
+ await new Promise((resolve, reject) => {
70
+ const process = spawn("pdftoppm", [
71
+ "-jpeg",
72
+ tempFilePath,
73
+ outputPrefix,
74
+ ]);
75
+ process.on("close", (code) => {
76
+ if (code === 0) resolve();
77
+ else
78
+ reject(new Error(`pdftoppm process exited with code ${code}`));
79
+ });
80
+ });
81
+
82
+ // Get a list of extracted images
83
+ const files = fs.readdirSync(outputDir);
84
+ const images = files.filter(
85
+ (file) => file.startsWith("page") && file.endsWith(".jpg")
86
+ );
87
+
88
+ return images.map((image) => {
89
+ return {
90
+ type: "jpeg",
91
+ media_type: this.getMimeType("jpeg"),
92
+ path: path.join(outputDir, image),
93
+ };
94
+ });
95
+ } else {
96
+ throw new Error(
97
+ "Unsupported file type. Only PDF, JPEG, and PNG are supported."
98
+ );
99
+ }
100
+ }
101
+ } catch (error) {
102
+ console.error("Error processing file:", error);
103
+ throw error;
104
+ } finally {
105
+ // Clean up the temporary input file
106
+ if (fs.existsSync(tempFilePath)) {
107
+ fs.unlinkSync(tempFilePath);
108
+ }
109
+ }
110
+ }
111
+
112
+ getMimeType(fileType) {
113
+ // Mapping of file extensions to MIME types
114
+ const mimeTypes = {
115
+ jpeg: "image/jpeg",
116
+ jpg: "image/jpeg", // Adding 'jpg' as it's a common alias for 'jpeg'
117
+ png: "image/png",
118
+ gif: "image/gif",
119
+ bmp: "image/bmp",
120
+ webp: "image/webp",
121
+ svg: "image/svg+xml",
122
+ pdf: "application/pdf",
123
+ txt: "text/plain",
124
+ html: "text/html",
125
+ css: "text/css",
126
+ js: "application/javascript",
127
+ json: "application/json",
128
+ xml: "application/xml",
129
+ csv: "text/csv",
130
+ mp3: "audio/mpeg",
131
+ wav: "audio/wav",
132
+ mp4: "video/mp4",
133
+ avi: "video/x-msvideo",
134
+ mov: "video/quicktime",
135
+ zip: "application/zip",
136
+ rar: "application/vnd.rar",
137
+ "7z": "application/x-7z-compressed",
138
+ // Add more MIME types as needed
139
+ };
140
+
141
+ // Ensure the fileType is a string and convert it to lowercase
142
+ if (typeof fileType !== "string") {
143
+ throw new TypeError("fileType must be a string");
144
+ }
145
+
146
+ const ext = fileType.toLowerCase();
147
+
148
+ // Return the MIME type if found, otherwise return the default
149
+ return mimeTypes[ext] || "application/octet-stream";
150
+ }
151
+
152
+ async getFileType(filePath) {
153
+ return new Promise((resolve, reject) => {
154
+ const fileSignature = Buffer.alloc(8);
155
+ fs.open(filePath, "r", (err, fd) => {
156
+ if (err) reject(err);
157
+ fs.read(fd, fileSignature, 0, 8, 0, (err) => {
158
+ if (err) reject(err);
159
+ fs.close(fd, (err) => {
160
+ if (err) console.error("Error closing file:", err);
161
+ });
162
+
163
+ const hex = fileSignature.toString("hex");
164
+ if (hex.startsWith("ffd8")) {
165
+ resolve("jpeg");
166
+ } else if (hex.startsWith("89504e47")) {
167
+ resolve("png");
168
+ } else if (fileSignature.toString("ascii").startsWith("%PDF")) {
169
+ resolve("pdf");
170
+ } else {
171
+ resolve("unknown");
172
+ }
173
+ });
174
+ });
175
+ });
176
+ }
177
+
178
+ async deleteFolderRecursive(dirPath) {
179
+ if (fs.existsSync(dirPath)) {
180
+ for (const entry of fs.readdirSync(dirPath)) {
181
+ const entryPath = path.join(dirPath, entry);
182
+ if (fs.lstatSync(entryPath).isDirectory()) {
183
+ await this.deleteFolderRecursive(entryPath);
184
+ } else {
185
+ await fs.promises.unlink(entryPath);
186
+ }
187
+ }
188
+ await fs.promises.rmdir(dirPath);
189
+ }
190
+ }
191
+
192
+ async processFiles(files) {
193
+ let types = this.getSupportedFileTypes();
194
+ const outputDir = this.createUniqueDirectory();
195
+
196
+ return (
197
+ await Promise.all(
198
+ files.map(async (fileStream) => {
199
+ //let type = await this.getFileType(fileStream);
200
+ const imagePaths = await this.extractOrStoreFile(
201
+ fileStream,
202
+ outputDir,
203
+ types
204
+ );
205
+ return imagePaths;
206
+ })
207
+ )
208
+ ).flat();
209
+ }
210
+
211
+ getSupportedFileTypes(options) {
212
+ if (this.Provider === undefined) {
213
+ throw new Error(`AI provider ${this.provider} is not defined`);
214
+ }
215
+ let provider = new this.Provider(this.options);
216
+ return provider.getSupportedTypes();
217
+ }
218
+
219
+ async prompt(prompt, { files, system_message, prompt_options }, options) {
220
+ let context_files = await this.processFiles(files);
221
+ try {
222
+ if (options) {
223
+ this.options = _.merge(this.options, options);
224
+ }
225
+ //console.log(this.Provider);
226
+ if (this.Provider === undefined) {
227
+ throw new Error(`AI provider ${this.provider} is not defined`);
228
+ }
229
+ let provider = new this.Provider(this.options);
230
+ return await provider.prompt(prompt, {
231
+ prompt_options,
232
+ system_message,
233
+ files: context_files,
234
+ });
235
+ } catch (e) {
236
+ console.error(e);
237
+ return { error: e.message };
238
+ } finally {
239
+ for (let context_file of context_files) {
240
+ await this.deleteFolderRecursive(context_file);
241
+ }
242
+ }
243
+ }
244
+ }
@@ -0,0 +1,3 @@
1
+ module.exports = {
2
+
3
+ }
package/src/index.js CHANGED
@@ -7,7 +7,7 @@ global.spice = {};
7
7
  require("dotenv").config({ path: path.resolve("./") });
8
8
 
9
9
  var Validate = require("koa-validation");
10
- const koaBody = require("koa-body");
10
+ const { koaBody } = require("koa-body");
11
11
  spice.parent_app_dir = path.resolve("./");
12
12
  spice.root_path = path.resolve("./build/");
13
13
  spice.module_root_path = path.resolve(__dirname);
@@ -33,6 +33,7 @@ export { default as MailDebug } from "./mail/providers/Debug";
33
33
  export { default as DebugStorage } from "./storage/providers/Debug";
34
34
  export { default as LocalStorage } from "./storage/providers/Local";
35
35
  export { default as Storage } from "./storage/Storage";
36
+ export { default as AI } from "./artificial_intelligence/Ai.js";
36
37
 
37
38
  export { default as MailFile } from "./mail/providers/File";
38
39
  export { default as SpiceCache } from "./cache/providers/SpiceCache";
@@ -1,32 +1,32 @@
1
- let fs = require('fs-extra');
2
- var path = require('path');
3
- var open = require("open");
1
+ let fs = require("fs-extra");
2
+ var path = require("path");
4
3
 
5
- export default class File{
6
-
7
- constructor(args={}){
8
- this.sender = args.sender;
9
- this.subject = args.subject;
10
- this.htmlText = args.htmlText;
11
- this.dest = path.join(path.resolve('./'),'storage/mail');
12
- if(args.path){
13
- this.dest = path.resolve(args.path)
14
- }
4
+ export default class File {
5
+ constructor(args = {}) {
6
+ this.sender = args.sender;
7
+ this.subject = args.subject;
8
+ this.htmlText = args.htmlText;
9
+ this.dest = path.join(path.resolve("./"), "storage/mail");
10
+ if (args.path) {
11
+ this.dest = path.resolve(args.path);
15
12
  }
13
+ }
16
14
 
17
- async send(htmlText, options={}){
15
+ async send(htmlText, options = {}) {
16
+ const openModule = await import("open");
17
+ const open = openModule.default;
18
+ let email = {
19
+ to: options.to,
20
+ from: options.sender || this.sender,
21
+ subject: options.subject || this.subject,
22
+ htmlText: htmlText || this.htmlText,
23
+ };
18
24
 
19
- let email = {
20
- to: options.to,
21
- from: options.sender || this.sender,
22
- subject: options.subject || this.subject,
23
- htmlText: htmlText || this.htmlText
24
- }
25
-
26
- let time = new Date().getTime();
27
- fs.ensureDirSync(`${this.dest}/${email.subject}/${time}`);
28
- let full_path = `${this.dest}/${email.subject}/${time}/to:${email.to} from:${email.from}.html`;
29
- fs.writeFileSync(full_path, email.htmlText);
30
- open(full_path);
31
- }
32
- }
25
+ let time = new Date().getTime();
26
+ fs.ensureDirSync(`${this.dest}/${email.subject}/${time}`);
27
+ let full_path = `${this.dest}/${email.subject}/${time}/to:${email.to} from:${email.from}.html`;
28
+ fs.writeFileSync(full_path, email.htmlText);
29
+
30
+ open(full_path);
31
+ }
32
+ }