@promptbook/cli 0.85.0-9 → 0.85.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/README.md CHANGED
@@ -24,10 +24,6 @@
24
24
 
25
25
 
26
26
 
27
- <blockquote style="color: #ff8811">
28
- <b>⚠ Warning:</b> This is a pre-release version of the library. It is not yet ready for production use. Please look at <a href="https://www.npmjs.com/package/@promptbook/core?activeTab=versions">latest stable release</a>.
29
- </blockquote>
30
-
31
27
  ## 📦 Package `@promptbook/cli`
32
28
 
33
29
  - Promptbooks are [divided into several](#-packages) packages, all are published from [single monorepo](https://github.com/webgptorg/promptbook).
package/esm/index.es.js CHANGED
@@ -2,7 +2,7 @@ import colors from 'colors';
2
2
  import commander from 'commander';
3
3
  import spaceTrim, { spaceTrim as spaceTrim$1 } from 'spacetrim';
4
4
  import { forTime, forEver } from 'waitasecond';
5
- import { basename, join, dirname } from 'path';
5
+ import { basename, join, dirname, relative } from 'path';
6
6
  import { stat, access, constants, readFile, writeFile, readdir, mkdir, unlink, rm, rmdir, rename } from 'fs/promises';
7
7
  import hexEncoder from 'crypto-js/enc-hex';
8
8
  import sha256 from 'crypto-js/sha256';
@@ -43,7 +43,7 @@ var BOOK_LANGUAGE_VERSION = '1.0.0';
43
43
  * @generated
44
44
  * @see https://github.com/webgptorg/promptbook
45
45
  */
46
- var PROMPTBOOK_ENGINE_VERSION = '0.85.0-8';
46
+ var PROMPTBOOK_ENGINE_VERSION = '0.85.0-16';
47
47
  /**
48
48
  * TODO: string_promptbook_version should be constrained to the all versions of Promptbook engine
49
49
  * Note: [💞] Ignore a discrepancy between file name and entity name
@@ -4059,57 +4059,6 @@ function isValidPromptbookVersion(version) {
4059
4059
  return true;
4060
4060
  }
4061
4061
 
4062
- /**
4063
- * Checks if an URL is reserved for private networks or localhost.
4064
- *
4065
- * Note: There are two simmilar functions:
4066
- * - `isUrlOnPrivateNetwork` which tests full URL
4067
- * - `isHostnameOnPrivateNetwork` *(this one)* which tests just hostname
4068
- *
4069
- * @public exported from `@promptbook/utils`
4070
- */
4071
- function isHostnameOnPrivateNetwork(hostname) {
4072
- if (hostname === 'example.com' ||
4073
- hostname === 'localhost' ||
4074
- hostname.endsWith('.localhost') ||
4075
- hostname.endsWith('.local') ||
4076
- hostname.endsWith('.test') ||
4077
- hostname === '127.0.0.1' ||
4078
- hostname === '::1') {
4079
- return true;
4080
- }
4081
- if (hostname.includes(':')) {
4082
- // IPv6
4083
- var ipParts = hostname.split(':');
4084
- return ipParts[0] === 'fc00' || ipParts[0] === 'fd00' || ipParts[0] === 'fe80';
4085
- }
4086
- else {
4087
- // IPv4
4088
- var ipParts = hostname.split('.').map(function (part) { return Number.parseInt(part, 10); });
4089
- return (ipParts[0] === 10 ||
4090
- (ipParts[0] === 172 && ipParts[1] >= 16 && ipParts[1] <= 31) ||
4091
- (ipParts[0] === 192 && ipParts[1] === 168));
4092
- }
4093
- }
4094
-
4095
- /**
4096
- * Checks if an IP address or hostname is reserved for private networks or localhost.
4097
- *
4098
- * Note: There are two simmilar functions:
4099
- * - `isUrlOnPrivateNetwork` *(this one)* which tests full URL
4100
- * - `isHostnameOnPrivateNetwork` which tests just hostname
4101
- *
4102
- * @param {string} ipAddress - The IP address to check.
4103
- * @returns {boolean} Returns true if the IP address is reserved for private networks or localhost, otherwise false.
4104
- * @public exported from `@promptbook/utils`
4105
- */
4106
- function isUrlOnPrivateNetwork(url) {
4107
- if (typeof url === 'string') {
4108
- url = new URL(url);
4109
- }
4110
- return isHostnameOnPrivateNetwork(url.hostname);
4111
- }
4112
-
4113
4062
  /**
4114
4063
  * Tests if given string is valid pipeline URL URL.
4115
4064
  *
@@ -4123,16 +4072,19 @@ function isValidPipelineUrl(url) {
4123
4072
  if (!isValidUrl(url)) {
4124
4073
  return false;
4125
4074
  }
4126
- if (!url.startsWith('https://')) {
4075
+ if (!url.startsWith('https://') && !url.startsWith('http://') /* <- Note: [👣] */) {
4127
4076
  return false;
4128
4077
  }
4129
4078
  if (url.includes('#')) {
4130
4079
  // TODO: [🐠]
4131
4080
  return false;
4132
4081
  }
4082
+ /*
4083
+ Note: [👣][🧠] Is it secure to allow pipeline URLs on private and unsecured networks?
4133
4084
  if (isUrlOnPrivateNetwork(url)) {
4134
4085
  return false;
4135
4086
  }
4087
+ */
4136
4088
  return true;
4137
4089
  }
4138
4090
  /**
@@ -12029,13 +11981,13 @@ function createCollectionFromPromise(promptbookSourcesPromiseOrFactory) {
12029
11981
  *
12030
11982
  * Note: Works only in Node.js environment because it reads the file system
12031
11983
  *
12032
- * @param path - path to the directory with pipelines
11984
+ * @param rootPath - path to the directory with pipelines
12033
11985
  * @param tools - Execution tools to be used for pipeline preparation if needed - If not provided, `$provideExecutionToolsForNode` will be used
12034
11986
  * @param options - Options for the collection creation
12035
11987
  * @returns PipelineCollection
12036
11988
  * @public exported from `@promptbook/node`
12037
11989
  */
12038
- function createCollectionFromDirectory(path, tools, options) {
11990
+ function createCollectionFromDirectory(rootPath, tools, options) {
12039
11991
  return __awaiter(this, void 0, void 0, function () {
12040
11992
  var madeLibraryFilePath, _a, _b, isRecursive, _c, isVerbose, _d, isLazyLoaded, _e, isCrashedOnError, rootUrl, collection;
12041
11993
  var _this = this;
@@ -12052,7 +12004,7 @@ function createCollectionFromDirectory(path, tools, options) {
12052
12004
  throw new EnvironmentMismatchError('Can not create collection without filesystem tools');
12053
12005
  // <- TODO: [🧠] What is the best error type here`
12054
12006
  }
12055
- madeLibraryFilePath = join(path, "".concat(DEFAULT_PIPELINE_COLLECTION_BASE_FILENAME
12007
+ madeLibraryFilePath = join(rootPath, "".concat(DEFAULT_PIPELINE_COLLECTION_BASE_FILENAME
12056
12008
  // <- TODO: [🦒] Allow to override (pass different value into the function)
12057
12009
  , ".json"));
12058
12010
  return [4 /*yield*/, isFileExisting(madeLibraryFilePath, tools.fs)];
@@ -12071,18 +12023,18 @@ function createCollectionFromDirectory(path, tools, options) {
12071
12023
  switch (_b.label) {
12072
12024
  case 0:
12073
12025
  if (isVerbose) {
12074
- console.info(colors.cyan("Creating pipeline collection from path ".concat(path.split('\\').join('/'))));
12026
+ console.info(colors.cyan("Creating pipeline collection from path ".concat(rootPath.split('\\').join('/'))));
12075
12027
  }
12076
- return [4 /*yield*/, listAllFiles(path, isRecursive, tools.fs)];
12028
+ return [4 /*yield*/, listAllFiles(rootPath, isRecursive, tools.fs)];
12077
12029
  case 1:
12078
12030
  fileNames = _b.sent();
12079
- // Note: First load all .book.json and then .book.md files
12080
- // .book.json can be prepared so it is faster to load
12031
+ // Note: First load all `.book.json` and then `.book` / `.book.md` files
12032
+ // `.book.json` can be prepared so it is faster to load
12081
12033
  fileNames.sort(function (a, b) {
12082
- if (a.endsWith('.json') && b.endsWith('.md')) {
12034
+ if (a.endsWith('.json') && (b.endsWith('.book') || b.endsWith('.book.md'))) {
12083
12035
  return -1;
12084
12036
  }
12085
- if (a.endsWith('.md') && b.endsWith('.json')) {
12037
+ if ((a.endsWith('.book') || a.endsWith('.book.md')) && b.endsWith('.json')) {
12086
12038
  return 1;
12087
12039
  }
12088
12040
  return 0;
@@ -12099,7 +12051,7 @@ function createCollectionFromDirectory(path, tools, options) {
12099
12051
  case 1:
12100
12052
  _f.trys.push([1, 8, , 9]);
12101
12053
  pipeline = null;
12102
- if (!fileName.endsWith('.book.md')) return [3 /*break*/, 4];
12054
+ if (!(fileName.endsWith('.book') || fileName.endsWith('.book.md'))) return [3 /*break*/, 4];
12103
12055
  _c = validatePipelineString;
12104
12056
  return [4 /*yield*/, readFile(fileName, 'utf-8')];
12105
12057
  case 2:
@@ -12131,7 +12083,8 @@ function createCollectionFromDirectory(path, tools, options) {
12131
12083
  if (pipeline !== null) {
12132
12084
  if (rootUrl !== undefined) {
12133
12085
  if (pipeline.pipelineUrl === undefined) {
12134
- pipelineUrl = rootUrl + '/' + fileName.split('\\').join('/');
12086
+ pipelineUrl = rootUrl + '/' + relative(rootPath, fileName).split('\\').join('/');
12087
+ // console.log({ pipelineUrl, rootPath, rootUrl, fileName });
12135
12088
  if (isVerbose) {
12136
12089
  console.info(colors.yellow("Implicitly set pipeline URL to ".concat(pipelineUrl, " from ").concat(fileName
12137
12090
  .split('\\')
@@ -13915,6 +13868,46 @@ function startRemoteServer(options) {
13915
13868
  }
13916
13869
  });
13917
13870
  }); });
13871
+ // TODO: [🧠] Is it secure / good idea to expose source codes of hosted books
13872
+ app.get("".concat(rootPath, "/books/*"), function (request, response) { return __awaiter(_this, void 0, void 0, function () {
13873
+ var pipelines, fullUrl, pipelineUrl, pipeline, source, error_1;
13874
+ return __generator(this, function (_a) {
13875
+ switch (_a.label) {
13876
+ case 0:
13877
+ _a.trys.push([0, 3, , 4]);
13878
+ if (collection === null) {
13879
+ response.status(500).send('No collection nor books available');
13880
+ return [2 /*return*/];
13881
+ }
13882
+ return [4 /*yield*/, collection.listPipelines()];
13883
+ case 1:
13884
+ pipelines = _a.sent();
13885
+ fullUrl = request.protocol + '://' + request.get('host') + request.originalUrl;
13886
+ pipelineUrl = pipelines.find(function (pipelineUrl) { return pipelineUrl.endsWith(request.originalUrl); }) || fullUrl;
13887
+ return [4 /*yield*/, collection.getPipelineByUrl(pipelineUrl)];
13888
+ case 2:
13889
+ pipeline = _a.sent();
13890
+ source = pipeline.sources[0];
13891
+ if (source === undefined || source.type !== 'BOOK') {
13892
+ throw new Error('Pipeline source is not a book');
13893
+ }
13894
+ response
13895
+ .type('text/markdown')
13896
+ .send(source.content);
13897
+ return [3 /*break*/, 4];
13898
+ case 3:
13899
+ error_1 = _a.sent();
13900
+ if (!(error_1 instanceof Error)) {
13901
+ throw error_1;
13902
+ }
13903
+ response
13904
+ .status(404)
13905
+ .send({ error: serializeError(error_1) });
13906
+ return [3 /*break*/, 4];
13907
+ case 4: return [2 /*return*/];
13908
+ }
13909
+ });
13910
+ }); });
13918
13911
  app.get("".concat(rootPath, "/executions"), function (request, response) { return __awaiter(_this, void 0, void 0, function () {
13919
13912
  return __generator(this, function (_a) {
13920
13913
  response.send(runningExecutionTasks);
@@ -13927,7 +13920,9 @@ function startRemoteServer(options) {
13927
13920
  taskId = request.params.taskId;
13928
13921
  execution = runningExecutionTasks.find(function (executionTask) { return executionTask.taskId === taskId; });
13929
13922
  if (execution === undefined) {
13930
- response.status(404).send("Execution \"".concat(taskId, "\" not found"));
13923
+ response
13924
+ .status(404)
13925
+ .send("Execution \"".concat(taskId, "\" not found"));
13931
13926
  return [2 /*return*/];
13932
13927
  }
13933
13928
  response.send(execution.currentValue);
@@ -13935,7 +13930,7 @@ function startRemoteServer(options) {
13935
13930
  });
13936
13931
  }); });
13937
13932
  app.post("".concat(rootPath, "/executions/new"), function (request, response) { return __awaiter(_this, void 0, void 0, function () {
13938
- var _a, inputParameters, identification, pipelineUrl, pipeline, tools, pipelineExecutor, executionTask, error_1;
13933
+ var _a, inputParameters, identification, pipelineUrl, pipeline, tools, pipelineExecutor, executionTask, error_2;
13939
13934
  return __generator(this, function (_b) {
13940
13935
  switch (_b.label) {
13941
13936
  case 0:
@@ -13963,11 +13958,11 @@ function startRemoteServer(options) {
13963
13958
  response.send(executionTask);
13964
13959
  return [3 /*break*/, 5];
13965
13960
  case 4:
13966
- error_1 = _b.sent();
13967
- if (!(error_1 instanceof Error)) {
13968
- throw error_1;
13961
+ error_2 = _b.sent();
13962
+ if (!(error_2 instanceof Error)) {
13963
+ throw error_2;
13969
13964
  }
13970
- response.status(400).send({ error: serializeError(error_1) });
13965
+ response.status(400).send({ error: serializeError(error_2) });
13971
13966
  return [3 /*break*/, 5];
13972
13967
  case 5: return [2 /*return*/];
13973
13968
  }
@@ -13988,7 +13983,7 @@ function startRemoteServer(options) {
13988
13983
  }
13989
13984
  // -----------
13990
13985
  socket.on('prompt-request', function (request) { return __awaiter(_this, void 0, void 0, function () {
13991
- var identification, prompt, tools, llm, _a, promptResult, _b, error_2;
13986
+ var identification, prompt, tools, llm, _a, promptResult, _b, error_3;
13992
13987
  return __generator(this, function (_c) {
13993
13988
  switch (_c.label) {
13994
13989
  case 0:
@@ -14057,11 +14052,11 @@ function startRemoteServer(options) {
14057
14052
  socket.emit('prompt-response', { promptResult: promptResult } /* <- Note: [🤛] */);
14058
14053
  return [3 /*break*/, 15];
14059
14054
  case 13:
14060
- error_2 = _c.sent();
14061
- if (!(error_2 instanceof Error)) {
14062
- throw error_2;
14055
+ error_3 = _c.sent();
14056
+ if (!(error_3 instanceof Error)) {
14057
+ throw error_3;
14063
14058
  }
14064
- socket.emit('error', serializeError(error_2) /* <- Note: [🤛] */);
14059
+ socket.emit('error', serializeError(error_3) /* <- Note: [🤛] */);
14065
14060
  return [3 /*break*/, 15];
14066
14061
  case 14:
14067
14062
  socket.disconnect();
@@ -14073,7 +14068,7 @@ function startRemoteServer(options) {
14073
14068
  // -----------
14074
14069
  // TODO: [👒] Listing models (and checking configuration) probbably should go through REST API not Socket.io
14075
14070
  socket.on('listModels-request', function (request) { return __awaiter(_this, void 0, void 0, function () {
14076
- var identification, tools, llm, models, error_3;
14071
+ var identification, tools, llm, models, error_4;
14077
14072
  return __generator(this, function (_a) {
14078
14073
  switch (_a.label) {
14079
14074
  case 0:
@@ -14094,11 +14089,11 @@ function startRemoteServer(options) {
14094
14089
  socket.emit('listModels-response', { models: models } /* <- Note: [🤛] */);
14095
14090
  return [3 /*break*/, 6];
14096
14091
  case 4:
14097
- error_3 = _a.sent();
14098
- if (!(error_3 instanceof Error)) {
14099
- throw error_3;
14092
+ error_4 = _a.sent();
14093
+ if (!(error_4 instanceof Error)) {
14094
+ throw error_4;
14100
14095
  }
14101
- socket.emit('error', serializeError(error_3));
14096
+ socket.emit('error', serializeError(error_4));
14102
14097
  return [3 /*break*/, 6];
14103
14098
  case 5:
14104
14099
  socket.disconnect();
@@ -14110,7 +14105,7 @@ function startRemoteServer(options) {
14110
14105
  // -----------
14111
14106
  // TODO: [👒] Listing models (and checking configuration) probbably should go through REST API not Socket.io
14112
14107
  socket.on('preparePipeline-request', function (request) { return __awaiter(_this, void 0, void 0, function () {
14113
- var identification, pipeline, tools, preparedPipeline, error_4;
14108
+ var identification, pipeline, tools, preparedPipeline, error_5;
14114
14109
  return __generator(this, function (_a) {
14115
14110
  switch (_a.label) {
14116
14111
  case 0:
@@ -14130,11 +14125,11 @@ function startRemoteServer(options) {
14130
14125
  socket.emit('preparePipeline-response', { preparedPipeline: preparedPipeline } /* <- Note: [🤛] */);
14131
14126
  return [3 /*break*/, 6];
14132
14127
  case 4:
14133
- error_4 = _a.sent();
14134
- if (!(error_4 instanceof Error)) {
14135
- throw error_4;
14128
+ error_5 = _a.sent();
14129
+ if (!(error_5 instanceof Error)) {
14130
+ throw error_5;
14136
14131
  }
14137
- socket.emit('error', serializeError(error_4));
14132
+ socket.emit('error', serializeError(error_5));
14138
14133
  return [3 /*break*/, 6];
14139
14134
  case 5:
14140
14135
  socket.disconnect();