botium-core 1.15.9 → 1.15.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.
Files changed (85) hide show
  1. package/dist/botium-cjs.js +215 -55
  2. package/dist/botium-cjs.js.map +1 -1
  3. package/dist/botium-es.js +215 -54
  4. package/dist/botium-es.js.map +1 -1
  5. package/package.json +35 -40
  6. package/src/BotDriver.js +1 -1
  7. package/src/Events.js +1 -3
  8. package/src/containers/GridContainer.js +0 -4
  9. package/src/helpers/TranscriptUtils.js +66 -0
  10. package/src/scripting/Convo.js +9 -0
  11. package/src/scripting/ScriptingProvider.js +36 -5
  12. package/src/scripting/logichook/userinput/MediaInput.js +10 -2
  13. package/src/utils/boolean.js +39 -0
  14. package/test/compiler/compilercsv.spec.js +34 -0
  15. package/test/compiler/compilermarkdown.spec.js +11 -0
  16. package/test/compiler/compilertxt.spec.js +21 -0
  17. package/test/compiler/compilerxlsx.spec.js +27 -0
  18. package/test/compiler/precompilerjson.spec.js +1 -0
  19. package/test/compiler/precompilermarkdownrasa.spec.js +1 -0
  20. package/test/connectors/pluginconnectorcontainer.spec.js +1 -0
  21. package/test/connectors/simplerest.spec.js +3 -1
  22. package/test/convo/failure.spec.js +1 -0
  23. package/test/convo/fillAndApplyScriptingMemory.spec.js +56 -0
  24. package/test/convo/partialconvo.spec.js +3 -0
  25. package/test/convo/retry.spec.js +9 -0
  26. package/test/convo/retryasserter.spec.js +9 -0
  27. package/test/convo/transcript.spec.js +33 -0
  28. package/test/convo/tree.spec.js +5 -0
  29. package/test/driver/capabilities.spec.js +16 -0
  30. package/test/helpers/capabilitiesutils.spec.js +8 -0
  31. package/test/helpers/transcriptutils.spec.js +1 -0
  32. package/test/hooks/customhooks.spec.js +3 -0
  33. package/test/logichooks/hookfromsrc.spec.js +3 -0
  34. package/test/logichooks/textfromhook.spec.js +1 -0
  35. package/test/plugins/plugins.spec.js +3 -0
  36. package/test/scripting/asserters/buttonsAsserter.spec.js +15 -0
  37. package/test/scripting/asserters/cardsAsserter.spec.js +12 -0
  38. package/test/scripting/asserters/convoStepParameters.spec.js +11 -0
  39. package/test/scripting/asserters/entitiesAsserter.spec.js +1 -0
  40. package/test/scripting/asserters/entityValuesAsserter.spec.js +7 -0
  41. package/test/scripting/asserters/formsAsserter.spec.js +10 -0
  42. package/test/scripting/asserters/intentAsserter.spec.js +4 -0
  43. package/test/scripting/asserters/intentUniqueAsserter.spec.js +2 -0
  44. package/test/scripting/asserters/jsonpathAsserter.spec.js +25 -0
  45. package/test/scripting/asserters/mediaAsserter.spec.js +20 -0
  46. package/test/scripting/asserters/responseLengthAsserter.spec.js +4 -0
  47. package/test/scripting/asserters/textAsserter.spec.js +1 -0
  48. package/test/scripting/asserters/textContainsAllAsserter.spec.js +1 -0
  49. package/test/scripting/asserters/textContainsAnyAsserter.spec.js +1 -0
  50. package/test/scripting/asserters/textEqualsAsserter.spec.js +1 -0
  51. package/test/scripting/asserters/textRegexpAllAsserter.spec.js +1 -0
  52. package/test/scripting/asserters/textRegexpAnyAsserter.spec.js +1 -0
  53. package/test/scripting/asserters/textWildcardAllAsserter.spec.js +1 -0
  54. package/test/scripting/asserters/textWildcardAnyAsserter.spec.js +1 -0
  55. package/test/scripting/asserters/textWildcardExactAllAsserter.spec.js +1 -0
  56. package/test/scripting/asserters/textWildcardExactAnyAsserter.spec.js +1 -0
  57. package/test/scripting/asserters/werAsserter.spec.js +6 -0
  58. package/test/scripting/logichooks/customConditionalStepLogicHook.spec.js +2 -0
  59. package/test/scripting/logichooks/localvsglobal.spec.js +1 -0
  60. package/test/scripting/logichooks/pauseLogic.spec.js +4 -0
  61. package/test/scripting/logichooks/setClearScriptingMemory.spec.js +3 -0
  62. package/test/scripting/logichooks/updateCustom.spec.js +2 -0
  63. package/test/scripting/matching/matchingmode.spec.js +48 -0
  64. package/test/scripting/scriptingModificator.spec.js +1 -0
  65. package/test/scripting/scriptingmemory/fillScriptingMemoryFromFile.spec.js +4 -0
  66. package/test/scripting/scriptingmemory/regexp.spec.js +1 -0
  67. package/test/scripting/scriptingmemory/useScriptingMemoryForAssertion.spec.js +3 -0
  68. package/test/scripting/txt/decompile.spec.js +20 -0
  69. package/test/scripting/userinputs/buttonInputConvos.spec.js +1 -0
  70. package/test/scripting/userinputs/defaultUserInputs.spec.js +13 -0
  71. package/test/scripting/userinputs/mediaInputConvos.spec.js +10 -0
  72. package/test/scripting/utteranceexpansion/associateByIndex.spec.js +2 -0
  73. package/test/security/allowUnsafe.spec.js +5 -0
  74. package/test/utils.spec.js +2 -0
  75. package/samples/postman/Botium Agent Sample.postman_collection.json +0 -834
  76. package/samples/postman/README.md +0 -5
  77. package/samples/postman/botiumFluent.js +0 -37
  78. package/src/grid/agent/AgentWorker.js +0 -204
  79. package/src/grid/agent/agent.js +0 -96
  80. package/src/grid/agent/agentworkerpool.js +0 -58
  81. package/src/grid/agent/routes.js +0 -353
  82. package/src/grid/agent/swagger.json +0 -327
  83. package/src/grid/agent/swaggerDef.json +0 -8
  84. package/src/grid/agent/views/index.html +0 -39
  85. package/test/grid/agent/client.js +0 -65
package/dist/botium-es.js CHANGED
@@ -8,7 +8,6 @@ import sanitizeFilename from 'sanitize-filename';
8
8
  import moment from 'moment';
9
9
  import randomatic from 'randomatic';
10
10
  import lodash from 'lodash';
11
- import boolean$1 from 'boolean';
12
11
  import events from 'events';
13
12
  import debug$o from 'debug';
14
13
  import isClass from 'is-class';
@@ -34,8 +33,49 @@ import undici from 'undici';
34
33
  import express from 'express';
35
34
  import bodyParser from 'body-parser';
36
35
 
36
+ /**
37
+ * Converts a value to a boolean.
38
+ * Similar to the 'boolean' npm package implementation.
39
+ *
40
+ * @param {*} value - The value to convert to boolean
41
+ * @returns {boolean} The boolean representation of the value
42
+ */
43
+ function boolean$1(value) {
44
+ // Handle null and undefined
45
+ if (value == null) {
46
+ return false;
47
+ }
48
+
49
+ // Handle boolean values
50
+ if (typeof value === 'boolean') {
51
+ return value;
52
+ }
53
+
54
+ // Handle numbers
55
+ if (typeof value === 'number') {
56
+ return value !== 0 && !isNaN(value);
57
+ }
58
+
59
+ // Handle strings
60
+ if (typeof value === 'string') {
61
+ const normalized = value.trim().toLowerCase();
62
+ if (normalized === 'true' || normalized === 'yes' || normalized === 'on' || normalized === '1') {
63
+ return true;
64
+ }
65
+ if (normalized === 'false' || normalized === 'no' || normalized === 'off' || normalized === '0' || normalized === '') {
66
+ return false;
67
+ }
68
+ }
69
+
70
+ // For all other values, use JavaScript's truthiness
71
+ return Boolean(value);
72
+ }
73
+ var boolean_1 = {
74
+ boolean: boolean$1
75
+ };
76
+
37
77
  var name = "botium-core";
38
- var version$1 = "1.15.9";
78
+ var version$1 = "1.15.12";
39
79
  var description = "The Selenium for Chatbots";
40
80
  var main = "index.js";
41
81
  var module = "dist/botium-es.js";
@@ -48,8 +88,6 @@ var scripts = {
48
88
  eslint: "eslint \"./src/**/*.js\" \"./test/**/*.js\"",
49
89
  "eslint-fix": "eslint --fix \"./src/**/*.js\" \"./test/**/*.js\"",
50
90
  newpatch: "npm version patch",
51
- agent: "node ./src/grid/agent/agent.js",
52
- "agent-jsdoc": "swagger-jsdoc -d ./src/grid/agent/swaggerDef.json -o ./src/grid/agent/swagger.json ./src/grid/agent/routes.js",
53
91
  link: "npm link botium-connector-dialogflow botium-connector-webdriverio botium-connector-directline3 botium-connector-watson botium-connector-alexa-smapi botium-connector-echo",
54
92
  test: "cross-env NODE_PATH=\"./test/plugins/plugindir/fromfolder:./test/plugins/plugindir/fromfile:./test/security/resources\" mocha \"./test/**/*.spec.js\"",
55
93
  "coverage:report": "nyc report --reporter=lcov npm test",
@@ -66,66 +104,63 @@ var bugs = {
66
104
  };
67
105
  var homepage = "https://www.botium.ai";
68
106
  var dependencies = {
69
- "@babel/runtime": "^7.23.9",
70
- async: "^3.2.5",
71
- "body-parser": "^1.20.2",
72
- boolean: "^3.2.0",
107
+ "@babel/runtime": "^7.28.6",
108
+ async: "^3.2.6",
109
+ "body-parser": "^2.2.2",
73
110
  bottleneck: "^2.19.5",
74
- "csv-parse": "^5.5.3",
75
- debug: "^4.3.4",
76
- express: "^4.18.2",
111
+ "csv-parse": "^6.1.0",
112
+ debug: "^4.4.3",
113
+ express: "^5.2.1",
77
114
  globby: "11.0.4",
78
- ioredis: "^5.3.2",
115
+ ioredis: "^5.10.0",
79
116
  "is-class": "^0.0.9",
80
117
  "is-json": "^2.0.1",
81
- jsonpath: "^1.1.1",
82
- lodash: "^4.17.21",
83
- "markdown-it": "^14.0.0",
84
- "mime-types": "^2.1.35",
118
+ jsonpath: "^1.3.0",
119
+ lodash: "^4.17.23",
120
+ "markdown-it": "^14.1.1",
121
+ "mime-types": "^3.0.2",
85
122
  mkdirp: "^3.0.1",
86
123
  moment: "^2.30.1",
87
- "moment-timezone": "^0.5.45",
124
+ "moment-timezone": "^0.6.0",
88
125
  mustache: "^4.2.0",
89
126
  "promise-retry": "^2.0.1",
90
127
  "promise.allsettled": "^1.0.7",
91
128
  randomatic: "^3.1.1",
92
- rimraf: "^5.0.5",
129
+ rimraf: "^6.1.3",
93
130
  "sanitize-filename": "^1.6.3",
94
131
  slugify: "^1.6.6",
95
- "socket.io": "^4.7.4",
96
- "socket.io-client": "^4.7.4",
132
+ "socket.io": "^4.8.3",
133
+ "socket.io-client": "^4.8.3",
97
134
  "socketio-auth": "^0.1.1",
98
- "swagger-jsdoc": "^6.2.8",
99
- "swagger-ui-express": "^5.0.0",
100
- tinyglobby: "^0.2.10",
101
- undici: "^6.21.0",
135
+ tinyglobby: "^0.2.15",
136
+ undici: "^7.22.0",
102
137
  uuid: "^9.0.1",
103
138
  "word-error-rate": "0.0.7",
104
139
  "write-yaml": "^1.0.0",
105
140
  xlsx: "^0.18.5",
106
- xregexp: "^5.1.1",
107
- yaml: "^2.3.4"
141
+ xregexp: "^5.1.2",
142
+ yaml: "^2.8.2"
108
143
  };
109
144
  var devDependencies = {
110
- "@babel/core": "^7.23.9",
111
- "@babel/node": "^7.23.9",
112
- "@babel/plugin-transform-runtime": "^7.23.9",
113
- "@babel/preset-env": "^7.23.9",
145
+ "@babel/core": "^7.29.0",
146
+ "@babel/node": "^7.29.0",
147
+ "@babel/plugin-transform-runtime": "^7.29.0",
148
+ "@babel/preset-env": "^7.29.0",
114
149
  chai: "4.3.10",
115
- "chai-as-promised": "^7.1.1",
116
- "cross-env": "^7.0.3",
117
- eslint: "^8.56.0",
150
+ "chai-as-promised": "^7.1.2",
151
+ "cross-env": "^10.1.0",
152
+ eslint: "^8.57.1",
118
153
  "eslint-config-standard": "^17.1.0",
119
- "eslint-plugin-import": "^2.29.1",
120
- "eslint-plugin-mocha": "^10.2.0",
154
+ "eslint-plugin-import": "^2.32.0",
155
+ "eslint-plugin-mocha": "^10.5.0",
121
156
  "eslint-plugin-n": "^16.6.2",
122
- "eslint-plugin-promise": "^6.1.1",
157
+ "eslint-plugin-promise": "^6.6.0",
123
158
  "eslint-plugin-standard": "^4.1.0",
124
- mocha: "^10.3.0",
125
- nock: "^14.0.0-beta.19",
126
- "npm-check-updates": "^16.14.15",
127
- nyc: "^15.1.0",
128
- rollup: "2.79.1",
159
+ mocha: "^11.7.5",
160
+ nock: "^14.0.11",
161
+ "npm-check-updates": "^19.6.3",
162
+ nyc: "^18.0.0",
163
+ rollup: "2.80.0",
129
164
  "rollup-plugin-babel": "^4.4.0",
130
165
  "rollup-plugin-commonjs": "^10.1.0",
131
166
  "rollup-plugin-json": "^4.0.0",
@@ -859,9 +894,7 @@ var Events = {
859
894
  MESSAGE_RECEIVEDFROMBOT: 'MESSAGE_RECEIVEDFROMBOT',
860
895
  MESSAGE_RECEIVEFROMBOT_ERROR: 'MESSAGE_RECEIVEFROMBOT_ERROR',
861
896
  MESSAGE_ATTACHMENT: 'MESSAGE_ATTACHMENT',
862
- MESSAGE_TRANSCRIPT: 'MESSAGE_TRANSCRIPT',
863
- // Botium Agent Events
864
- TOOMUCHWORKERS_ERROR: 'TOOMUCHWORKERS_ERROR'
897
+ MESSAGE_TRANSCRIPT: 'MESSAGE_TRANSCRIPT'
865
898
  };
866
899
  Events.CONTAINER_BUILDING;
867
900
  Events.CONTAINER_BUILT;
@@ -883,7 +916,6 @@ Events.MESSAGE_RECEIVEDFROMBOT;
883
916
  Events.MESSAGE_RECEIVEFROMBOT_ERROR;
884
917
  Events.MESSAGE_ATTACHMENT;
885
918
  Events.MESSAGE_TRANSCRIPT;
886
- Events.TOOMUCHWORKERS_ERROR;
887
919
 
888
920
  function commonjsRequire () {
889
921
  throw new Error('Dynamic requires are not currently supported by rollup-plugin-commonjs');
@@ -2997,6 +3029,7 @@ class Convo$6 {
2997
3029
  }
2998
3030
  async runConversation(container, scriptingMemory, transcript) {
2999
3031
  const transcriptSteps = [];
3032
+ transcript.steps = transcriptSteps;
3000
3033
  try {
3001
3034
  let lastMeConvoStep = null;
3002
3035
  let botMsg = null;
@@ -3181,6 +3214,21 @@ class Convo$6 {
3181
3214
  throw failErr;
3182
3215
  }
3183
3216
  } else if (convoStep.sender === 'bot') {
3217
+ if (this.scriptingEvents.executeBotStep) {
3218
+ const executeBotStepResult = await this.scriptingEvents.executeBotStep({
3219
+ convo: this,
3220
+ convoStep,
3221
+ container,
3222
+ scriptingMemory,
3223
+ transcript,
3224
+ transcriptStep,
3225
+ transcriptSteps
3226
+ });
3227
+ if (executeBotStepResult) {
3228
+ skipTranscriptStep = true;
3229
+ continue;
3230
+ }
3231
+ }
3184
3232
  if (waitForBotSays) {
3185
3233
  botMsg = null;
3186
3234
  } else {
@@ -5754,6 +5802,31 @@ var ScriptingProvider_1 = class ScriptingProvider {
5754
5802
  ...rest
5755
5803
  });
5756
5804
  },
5805
+ executeBotStep: ({
5806
+ convo,
5807
+ convoStep,
5808
+ container,
5809
+ scriptingMemory,
5810
+ ...rest
5811
+ }) => {
5812
+ const logicHooks = convoStep?.logicHooks || [];
5813
+ const executeBotStepHooks = logicHooks.filter(l => this.logicHooks[l.name] && typeof this.logicHooks[l.name].executeBotStep === 'function');
5814
+ if (executeBotStepHooks.length > 1) {
5815
+ throw new Error(`${convo?.header?.name}/${convoStep?.stepTag}: Multiple logic hooks implement executeBotStep: ${executeBotStepHooks.map(l => l.name).join(', ')}. Only one is allowed per step.`);
5816
+ }
5817
+ if (executeBotStepHooks.length === 1) {
5818
+ const lh = executeBotStepHooks[0];
5819
+ return this.logicHooks[lh.name].executeBotStep({
5820
+ convo,
5821
+ convoStep,
5822
+ scriptingMemory,
5823
+ container,
5824
+ args: ScriptingMemory.applyToArgs(lh.args, scriptingMemory, container.caps),
5825
+ ...rest
5826
+ });
5827
+ }
5828
+ return null;
5829
+ },
5757
5830
  assertConvoBegin: ({
5758
5831
  convo,
5759
5832
  convoStep,
@@ -6156,6 +6229,7 @@ var ScriptingProvider_1 = class ScriptingProvider {
6156
6229
  onBotStart: this.scriptingEvents.onBotStart.bind(this),
6157
6230
  onBotPrepare: this.scriptingEvents.onBotPrepare.bind(this),
6158
6231
  onBotEnd: this.scriptingEvents.onBotEnd.bind(this),
6232
+ executeBotStep: this.scriptingEvents.executeBotStep.bind(this),
6159
6233
  setUserInput: this.scriptingEvents.setUserInput.bind(this),
6160
6234
  fail: this.scriptingEvents.fail && this.scriptingEvents.fail.bind(this)
6161
6235
  }
@@ -6676,7 +6750,11 @@ var ScriptingProvider_1 = class ScriptingProvider {
6676
6750
  // use skip and keep, or justHeader
6677
6751
  justHeader: false,
6678
6752
  // drop unwanted convos
6679
- convoFilter: null
6753
+ convoFilter: null,
6754
+ mediaInput: {
6755
+ // MESSAGE_TEXT_FROM_FILENAME or MESSAGE_TEXT_FROM_TRANSCRIPTION or falsy
6756
+ messageTextMode: null
6757
+ }
6680
6758
  }, options);
6681
6759
  const expandedConvos = [];
6682
6760
  // The globalContext is going to keep the data even if the Object.assign which happening to create the myContext in _expandConvo function
@@ -6714,7 +6792,11 @@ var ScriptingProvider_1 = class ScriptingProvider {
6714
6792
  ExpandConvosIterable(options = {}) {
6715
6793
  options = Object.assign({
6716
6794
  // drop unwanted convos
6717
- convoFilter: null
6795
+ convoFilter: null,
6796
+ mediaInput: {
6797
+ // MESSAGE_TEXT_FROM_FILENAME or MESSAGE_TEXT_FROM_TRANSCRIPTION or falsy
6798
+ messageTextMode: null
6799
+ }
6718
6800
  }, options);
6719
6801
  // The globalContext is going to keep the data even if the Object.assign which happening to create the myContext in _expandConvo function
6720
6802
  const context = {
@@ -6861,7 +6943,8 @@ var ScriptingProvider_1 = class ScriptingProvider {
6861
6943
  const expandedUserInputs = userInput.expandConvo({
6862
6944
  convo: currentConvo,
6863
6945
  convoStep: currentStep,
6864
- args: ui.args
6946
+ args: ui.args,
6947
+ options
6865
6948
  });
6866
6949
  if (expandedUserInputs && expandedUserInputs.length > 0) {
6867
6950
  // let sampleinputs = expandedUserInputs
@@ -6873,9 +6956,16 @@ var ScriptingProvider_1 = class ScriptingProvider {
6873
6956
  }
6874
6957
  };
6875
6958
  const processSampleInput = function* (sampleinput, length, index, myContext, uiIndex) {
6959
+ const {
6960
+ messageText,
6961
+ ...userInput
6962
+ } = sampleinput;
6876
6963
  const currentStepsStack = convoStepsStack.slice();
6877
6964
  const currentStepMod = lodash.cloneDeep(currentStep);
6878
- currentStepMod.userInputs[uiIndex] = sampleinput;
6965
+ currentStepMod.userInputs[uiIndex] = userInput;
6966
+ if (messageText) {
6967
+ currentStepMod.messageText = messageText;
6968
+ }
6879
6969
  currentStepsStack.push(currentStepMod);
6880
6970
  const currentConvoLabeled = lodash.cloneDeep(currentConvo);
6881
6971
  if (length > 1) {
@@ -7709,10 +7799,6 @@ var GridContainer_1 = class GridContainer extends BaseContainer_1 {
7709
7799
  debug$6(`unauthorized ${err.message}`);
7710
7800
  socketComplete(`Grid Access not authorized: ${err.message}`);
7711
7801
  });
7712
- this.socket.on(Events.TOOMUCHWORKERS_ERROR, err => {
7713
- debug$6(`TOOMUCHWORKERS_ERROR ${err.message}`);
7714
- socketComplete(`Grid Access not possible: ${err.message}`);
7715
- });
7716
7802
  this.socket.on(Events.CONTAINER_BUILT, () => {
7717
7803
  debug$6(Events.CONTAINER_BUILT);
7718
7804
  socketComplete();
@@ -9343,7 +9429,7 @@ const {
9343
9429
  } = rimraf$1;
9344
9430
  const {
9345
9431
  boolean
9346
- } = boolean$1;
9432
+ } = boolean_1;
9347
9433
  const debug$1 = debug$o('botium-core-BotDriver');
9348
9434
  const {
9349
9435
  version
@@ -9795,6 +9881,79 @@ Plugins.PLUGIN_TYPE_LOGICHOOK;
9795
9881
  Plugins.PLUGIN_TYPE_USERINPUT;
9796
9882
  Plugins.TYPE_TO_PREFIX;
9797
9883
 
9884
+ const {
9885
+ parse: csvParseSync
9886
+ } = sync;
9887
+
9888
+ /**
9889
+ * Find transcription for an audio file: .txt same base name, or transcript.csv in parent dirs.
9890
+ * @param {string} baseDir - Base directory for resolving paths
9891
+ * @param {string} audioFile - Relative path to audio file
9892
+ * @param {object} [options] - Optional: { csvCache: {}, onError: (msg) => {} }
9893
+ * @returns {string|null} Transcription text or null
9894
+ */
9895
+ var findTranscription = (baseDir, audioFile, options = {}) => {
9896
+ const {
9897
+ csvCache = {},
9898
+ onError
9899
+ } = options;
9900
+ const transcriptionFilename = `${audioFile.substring(0, audioFile.lastIndexOf('.'))}.txt`;
9901
+ const transcriptionFilenameAbs = path.resolve(baseDir, transcriptionFilename);
9902
+ try {
9903
+ if (fs.existsSync(transcriptionFilenameAbs)) {
9904
+ return fs.readFileSync(transcriptionFilenameAbs, {
9905
+ encoding: 'utf-8'
9906
+ }).trim();
9907
+ }
9908
+ } catch (err) {
9909
+ if (onError) onError(`Transcription File ${transcriptionFilenameAbs} not readable: ${err.message}`);
9910
+ throw new Error(`Reading transcription file ${transcriptionFilename} for ${audioFile} failed`);
9911
+ }
9912
+ if (csvCache[audioFile]) {
9913
+ return csvCache[audioFile];
9914
+ }
9915
+ const audioFileComponents = audioFile.split('/');
9916
+ for (let parentIndex = audioFileComponents.length - 1; parentIndex >= 0; parentIndex--) {
9917
+ const csvDirectory = audioFileComponents.slice(0, parentIndex);
9918
+ const csvFilename = path.join(...csvDirectory, 'transcript.csv');
9919
+ const csvFilenameAbs = path.resolve(baseDir, csvFilename);
9920
+ try {
9921
+ if (fs.existsSync(csvFilenameAbs)) {
9922
+ const records = csvParseSync(fs.readFileSync(csvFilenameAbs, {
9923
+ encoding: 'utf-8'
9924
+ }).trim(), {
9925
+ columns: ['filename', 'transcription'],
9926
+ delimiter: [',', ';', ':', '\t'],
9927
+ trim: true,
9928
+ skip_empty_lines: true
9929
+ });
9930
+ if (records && records.length > 0) {
9931
+ for (const record of records) {
9932
+ const fnKey = path.join(...csvDirectory, record.filename);
9933
+ csvCache[fnKey] = record.transcription;
9934
+ }
9935
+ }
9936
+ }
9937
+ } catch (err) {
9938
+ if (onError) onError(`Transcription CSV File ${csvFilenameAbs} not readable: ${err.message}`);
9939
+ throw new Error(`Reading transcription CSV file for ${csvFilename} failed`);
9940
+ }
9941
+ if (csvCache[audioFile]) {
9942
+ return csvCache[audioFile];
9943
+ }
9944
+ }
9945
+ return null;
9946
+ };
9947
+
9948
+ /**
9949
+ * Derive transcription text from audio filename (basename without extension, underscores/hyphens → spaces).
9950
+ * @param {string} audioFile - Path or filename of audio file
9951
+ * @returns {string}
9952
+ */
9953
+ var transcriptionFromFilename = audioFile => {
9954
+ const filename = path.basename(audioFile, path.extname(audioFile));
9955
+ return filename.split(/[_-]+/).join(' ');
9956
+ };
9798
9957
  var hasWaitForBotTimeout = transciptError => {
9799
9958
  if (!transciptError) {
9800
9959
  return false;
@@ -9806,6 +9965,8 @@ var hasWaitForBotTimeout = transciptError => {
9806
9965
  return str.indexOf(': error waiting for bot - Bot did not respond within') > 0;
9807
9966
  };
9808
9967
  var TranscriptUtils = {
9968
+ findTranscription: findTranscription,
9969
+ transcriptionFromFilename: transcriptionFromFilename,
9809
9970
  hasWaitForBotTimeout: hasWaitForBotTimeout
9810
9971
  };
9811
9972