@promptbook/cli 0.100.0-1 → 0.100.0-2

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/esm/index.es.js CHANGED
@@ -47,7 +47,7 @@ const BOOK_LANGUAGE_VERSION = '1.0.0';
47
47
  * @generated
48
48
  * @see https://github.com/webgptorg/promptbook
49
49
  */
50
- const PROMPTBOOK_ENGINE_VERSION = '0.100.0-1';
50
+ const PROMPTBOOK_ENGINE_VERSION = '0.100.0-2';
51
51
  /**
52
52
  * TODO: string_promptbook_version should be constrained to the all versions of Promptbook engine
53
53
  * Note: [💞] Ignore a discrepancy between file name and entity name
@@ -221,12 +221,9 @@ const SMALL_NUMBER = 0.001;
221
221
  /**
222
222
  * Timeout for the connections in milliseconds
223
223
  *
224
- * Note: Increased from 7 seconds to 30 seconds to accommodate OAuth flows
225
- * like Facebook login which may require user interaction and redirects
226
- *
227
224
  * @private within the repository - too low-level in comparison with other `MAX_...`
228
225
  */
229
- const CONNECTION_TIMEOUT_MS = 30 * 1000;
226
+ const CONNECTION_TIMEOUT_MS = 7 * 1000;
230
227
  // <- TODO: [⏳] Standardize timeouts, Make DEFAULT_TIMEOUT_MS as global constant
231
228
  /**
232
229
  * How many times to retry the connections
@@ -2563,39 +2560,13 @@ async function createRemoteClient(options) {
2563
2560
  transports: ['polling', 'websocket' /*, <- TODO: [🌬] Allow to pass `transports`, add 'webtransport' */],
2564
2561
  });
2565
2562
  // console.log('Connecting to', this.options.remoteServerUrl.href, { socket });
2566
- let isResolved = false;
2567
2563
  socket.on('connect', () => {
2568
- if (!isResolved) {
2569
- isResolved = true;
2570
- resolve(socket);
2571
- }
2572
- });
2573
- socket.on('connect_error', (error) => {
2574
- if (!isResolved) {
2575
- isResolved = true;
2576
- reject(new Error(`Failed to connect to ${remoteServerUrl}: ${error.message || error}`));
2577
- }
2564
+ resolve(socket);
2578
2565
  });
2579
- socket.on('disconnect', (reason) => {
2580
- if (!isResolved) {
2581
- isResolved = true;
2582
- reject(new Error(`Connection to ${remoteServerUrl} was disconnected: ${reason}`));
2583
- }
2584
- });
2585
- // Better timeout handling with more descriptive error message
2586
- const timeoutId = setTimeout(() => {
2587
- if (!isResolved) {
2588
- isResolved = true;
2589
- socket.disconnect();
2590
- reject(new Error(`Connection timeout after ${CONNECTION_TIMEOUT_MS / 1000} seconds while connecting to ${remoteServerUrl}. ` +
2591
- `This may indicate network issues or the server may be experiencing high load. ` +
2592
- `For authentication flows like social login, ensure sufficient time is allowed for user interaction.`));
2593
- }
2566
+ // TODO: [💩] Better timeout handling
2567
+ setTimeout(() => {
2568
+ reject(new Error(`Timeout while connecting to ${remoteServerUrl}`));
2594
2569
  }, CONNECTION_TIMEOUT_MS);
2595
- // Clean up timeout if connection succeeds
2596
- socket.on('connect', () => {
2597
- clearTimeout(timeoutId);
2598
- });
2599
2570
  });
2600
2571
  }
2601
2572
 
@@ -2624,33 +2595,8 @@ class RemoteLlmExecutionTools {
2624
2595
  * Check the configuration of all execution tools
2625
2596
  */
2626
2597
  async checkConfiguration() {
2627
- try {
2628
- const socket = await createRemoteClient(this.options);
2629
- socket.disconnect();
2630
- }
2631
- catch (error) {
2632
- if (error instanceof Error) {
2633
- // Provide user-friendly error messages for common connection issues
2634
- if (error.message.includes('timeout') || error.message.includes('Timeout')) {
2635
- throw new Error(`Connection to Promptbook server timed out. This may happen during authentication flows like Facebook login. ` +
2636
- `Please ensure: 1) Server is running at ${this.options.remoteServerUrl}, ` +
2637
- `2) Network connection is stable, 3) Authentication process is completed within the timeout period. ` +
2638
- `Original error: ${error.message}`);
2639
- }
2640
- if (error.message.includes('connect') || error.message.includes('ECONNREFUSED')) {
2641
- throw new Error(`Cannot connect to Promptbook server at ${this.options.remoteServerUrl}. ` +
2642
- `Please check if the server is running and accessible. ` +
2643
- `Original error: ${error.message}`);
2644
- }
2645
- if (error.message.includes('authentication') || error.message.includes('auth')) {
2646
- throw new Error(`Authentication failed when connecting to Promptbook server. ` +
2647
- `This may happen if social login (like Facebook) was not completed properly. ` +
2648
- `Please retry the authentication process. ` +
2649
- `Original error: ${error.message}`);
2650
- }
2651
- }
2652
- throw error; // Re-throw if not a recognized error pattern
2653
- }
2598
+ const socket = await createRemoteClient(this.options);
2599
+ socket.disconnect();
2654
2600
  // TODO: [main] !!3 Check version of the remote server and compatibility
2655
2601
  // TODO: [🎍] Send checkConfiguration
2656
2602
  }
@@ -7765,7 +7711,36 @@ async function makeKnowledgeSourceHandler(knowledgeSource, tools, options) {
7765
7711
  if (fileContent.length > DEFAULT_MAX_FILE_SIZE /* <- TODO: Allow to pass different value to remote server */) {
7766
7712
  throw new LimitReachedError(`File is too large (${Math.round(fileContent.length / 1024 / 1024)}MB). Maximum allowed size is ${Math.round(DEFAULT_MAX_FILE_SIZE / 1024 / 1024)}MB.`);
7767
7713
  }
7768
- await tools.fs.writeFile(join(rootDirname, filepath), fileContent);
7714
+ // Note: Try to cache the downloaded file, but don't fail if the filesystem is read-only
7715
+ try {
7716
+ await tools.fs.writeFile(join(rootDirname, filepath), fileContent);
7717
+ }
7718
+ catch (error) {
7719
+ // Note: If we can't write to cache, we'll process the file directly from memory
7720
+ // This handles read-only filesystems like Vercel
7721
+ if (error instanceof Error && (error.message.includes('EROFS') ||
7722
+ error.message.includes('read-only') ||
7723
+ error.message.includes('EACCES') ||
7724
+ error.message.includes('EPERM'))) {
7725
+ // Return a handler that works directly with the downloaded content
7726
+ return {
7727
+ source: name,
7728
+ filename: null,
7729
+ url,
7730
+ mimeType,
7731
+ async asJson() {
7732
+ return JSON.parse(fileContent.toString('utf-8'));
7733
+ },
7734
+ async asText() {
7735
+ return fileContent.toString('utf-8');
7736
+ },
7737
+ };
7738
+ }
7739
+ else {
7740
+ // Re-throw other unexpected errors
7741
+ throw error;
7742
+ }
7743
+ }
7769
7744
  // TODO: [💵] Check the file security
7770
7745
  // TODO: [🧹][🧠] Delete the file after the scraping is done
7771
7746
  return makeKnowledgeSourceHandler({ name, knowledgeSourceContent: filepath }, tools, {
@@ -13382,7 +13357,22 @@ async function $getCompiledBook(tools, pipelineSource, options) {
13382
13357
  ...options,
13383
13358
  });
13384
13359
  const compiledFilePath = filePath.replace('.book.md', '.book').replace('.book', '.bookc');
13385
- await saveArchive(compiledFilePath, [pipelineJson], fs);
13360
+ // Note: Try to save the compiled book to disk for caching, but don't fail if the filesystem is read-only
13361
+ try {
13362
+ await saveArchive(compiledFilePath, [pipelineJson], fs);
13363
+ }
13364
+ catch (error) {
13365
+ // Note: Ignore filesystem errors (like EROFS on read-only systems like Vercel)
13366
+ // The compiled book can still be used even if it can't be cached
13367
+ if (error instanceof Error && (error.message.includes('EROFS') ||
13368
+ error.message.includes('read-only') ||
13369
+ error.message.includes('EACCES') ||
13370
+ error.message.includes('EPERM'))) ;
13371
+ else {
13372
+ // Re-throw other unexpected errors
13373
+ throw error;
13374
+ }
13375
+ }
13386
13376
  return pipelineJson;
13387
13377
  }
13388
13378
  }
@@ -14683,152 +14673,6 @@ function startRemoteServer(options) {
14683
14673
  response.status(400).send({ error: serializeError(error) });
14684
14674
  }
14685
14675
  });
14686
- // OAuth Authentication Endpoints
14687
- // These endpoints provide social authentication support (Facebook, Google, etc.)
14688
- app.get('/auth/:provider', async (request, response) => {
14689
- const { provider } = request.params;
14690
- if (!isApplicationModeAllowed) {
14691
- response.status(400).json({
14692
- error: 'Application mode is not allowed',
14693
- message: 'Social authentication requires application mode to be enabled'
14694
- });
14695
- return;
14696
- }
14697
- try {
14698
- // Get OAuth configuration from query params or environment
14699
- const { redirectUri, clientId, appId } = request.query;
14700
- if (!redirectUri || !clientId) {
14701
- response.status(400).json({
14702
- error: 'Missing OAuth parameters',
14703
- message: 'redirectUri and clientId are required for OAuth flow'
14704
- });
14705
- return;
14706
- }
14707
- let authUrl;
14708
- const state = Buffer.from(JSON.stringify({
14709
- appId: appId || 'default',
14710
- timestamp: Date.now()
14711
- })).toString('base64');
14712
- switch (provider.toLowerCase()) {
14713
- case 'facebook':
14714
- authUrl = `https://www.facebook.com/v18.0/dialog/oauth?` +
14715
- `client_id=${encodeURIComponent(clientId)}&` +
14716
- `redirect_uri=${encodeURIComponent(redirectUri)}&` +
14717
- `scope=email,public_profile&` +
14718
- `response_type=code&` +
14719
- `state=${encodeURIComponent(state)}`;
14720
- break;
14721
- case 'google':
14722
- authUrl = `https://accounts.google.com/o/oauth2/v2/auth?` +
14723
- `client_id=${encodeURIComponent(clientId)}&` +
14724
- `redirect_uri=${encodeURIComponent(redirectUri)}&` +
14725
- `scope=openid%20email%20profile&` +
14726
- `response_type=code&` +
14727
- `state=${encodeURIComponent(state)}`;
14728
- break;
14729
- default:
14730
- response.status(400).json({
14731
- error: 'Unsupported provider',
14732
- message: `Social authentication provider '${provider}' is not supported. Supported providers: facebook, google`
14733
- });
14734
- return;
14735
- }
14736
- // Log the OAuth attempt for debugging
14737
- if (isVerbose) {
14738
- console.info(colors.cyan(`OAuth ${provider} flow started for app ${appId || 'default'}`));
14739
- }
14740
- response.json({
14741
- authUrl,
14742
- provider,
14743
- state,
14744
- message: `Redirect user to authUrl to complete ${provider} authentication`
14745
- });
14746
- }
14747
- catch (error) {
14748
- assertsError(error);
14749
- console.warn(`OAuth ${provider} initialization failed:`, error);
14750
- response.status(500).json({
14751
- error: 'OAuth initialization failed',
14752
- message: error.message
14753
- });
14754
- }
14755
- });
14756
- app.post('/auth/:provider/callback', async (request, response) => {
14757
- const { provider } = request.params;
14758
- if (!isApplicationModeAllowed || login === null) {
14759
- response.status(400).json({
14760
- error: 'Application mode is not allowed',
14761
- message: 'Social authentication requires application mode and login handler to be configured'
14762
- });
14763
- return;
14764
- }
14765
- try {
14766
- const { code, state, error: oauthError } = request.body;
14767
- if (oauthError) {
14768
- response.status(400).json({
14769
- isSuccess: false,
14770
- error: 'OAuth authorization failed',
14771
- message: `${provider} authentication was denied or failed: ${oauthError}`
14772
- });
14773
- return;
14774
- }
14775
- if (!code || !state) {
14776
- response.status(400).json({
14777
- isSuccess: false,
14778
- error: 'Missing OAuth callback parameters',
14779
- message: 'code and state parameters are required'
14780
- });
14781
- return;
14782
- }
14783
- // Decode state to get app information
14784
- let appInfo;
14785
- try {
14786
- appInfo = JSON.parse(Buffer.from(state, 'base64').toString());
14787
- }
14788
- catch (_a) {
14789
- response.status(400).json({
14790
- isSuccess: false,
14791
- error: 'Invalid state parameter',
14792
- message: 'The OAuth state parameter is malformed'
14793
- });
14794
- return;
14795
- }
14796
- // Log the OAuth callback for debugging
14797
- if (isVerbose) {
14798
- console.info(colors.cyan(`OAuth ${provider} callback received for app ${appInfo.appId}`));
14799
- }
14800
- // Note: In a real implementation, you would:
14801
- // 1. Exchange the code for an access token with the OAuth provider
14802
- // 2. Use the access token to get user information
14803
- // 3. Create or find the user in your system
14804
- // 4. Call the login function with the user's information
14805
- // For now, we provide a framework that the implementer can extend
14806
- const mockUserInfo = {
14807
- username: `${provider}_user_${code.substring(0, 8)}`,
14808
- password: '',
14809
- appId: appInfo.appId
14810
- };
14811
- const loginResult = await login({
14812
- ...mockUserInfo,
14813
- rawRequest: request,
14814
- rawResponse: response,
14815
- });
14816
- response.status(200).json({
14817
- ...loginResult,
14818
- provider,
14819
- message: loginResult.message || `${provider} authentication completed`,
14820
- });
14821
- }
14822
- catch (error) {
14823
- assertsError(error);
14824
- console.warn(`OAuth ${provider} callback failed:`, error);
14825
- response.status(500).json({
14826
- isSuccess: false,
14827
- error: 'OAuth callback processing failed',
14828
- message: error.message
14829
- });
14830
- }
14831
- });
14832
14676
  app.get(`/books`, async (request, response) => {
14833
14677
  if (collection === null) {
14834
14678
  response.status(500).send('No collection available');
@@ -19673,7 +19517,22 @@ class MarkitdownScraper {
19673
19517
  // <- TODO: [🍀] Make MarkitdownError
19674
19518
  }
19675
19519
  // console.log('!!', { result, cacheFilehandler });
19676
- await this.tools.fs.writeFile(cacheFilehandler.filename, result.text_content);
19520
+ // Note: Try to cache the converted content, but don't fail if the filesystem is read-only
19521
+ try {
19522
+ await this.tools.fs.writeFile(cacheFilehandler.filename, result.text_content);
19523
+ }
19524
+ catch (error) {
19525
+ // Note: If we can't write to cache, we'll continue without caching
19526
+ // This handles read-only filesystems like Vercel
19527
+ if (error instanceof Error && (error.message.includes('EROFS') ||
19528
+ error.message.includes('read-only') ||
19529
+ error.message.includes('EACCES') ||
19530
+ error.message.includes('EPERM'))) ;
19531
+ else {
19532
+ // Re-throw other unexpected errors
19533
+ throw error;
19534
+ }
19535
+ }
19677
19536
  }
19678
19537
  return cacheFilehandler;
19679
19538
  }
@@ -19961,7 +19820,22 @@ class WebsiteScraper {
19961
19820
  extension: 'html',
19962
19821
  isVerbose,
19963
19822
  });
19964
- await this.tools.fs.writeFile(cacheFilehandler.filename, html, 'utf-8');
19823
+ // Note: Try to cache the scraped content, but don't fail if the filesystem is read-only
19824
+ try {
19825
+ await this.tools.fs.writeFile(cacheFilehandler.filename, html, 'utf-8');
19826
+ }
19827
+ catch (error) {
19828
+ // Note: If we can't write to cache, we'll continue without caching
19829
+ // This handles read-only filesystems like Vercel
19830
+ if (error instanceof Error && (error.message.includes('EROFS') ||
19831
+ error.message.includes('read-only') ||
19832
+ error.message.includes('EACCES') ||
19833
+ error.message.includes('EPERM'))) ;
19834
+ else {
19835
+ // Re-throw other unexpected errors
19836
+ throw error;
19837
+ }
19838
+ }
19965
19839
  const markdown = this.showdownConverter.makeMarkdown(html, jsdom.window.document);
19966
19840
  return { ...cacheFilehandler, markdown };
19967
19841
  }