@promptbook/cli 0.112.0-87 → 0.112.0-89

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.
@@ -1,4 +1,5 @@
1
1
  import type { NextConfig } from 'next';
2
+ import { readdirSync } from 'fs';
2
3
  import path from 'path';
3
4
 
4
5
  /**
@@ -9,6 +10,21 @@ import path from 'path';
9
10
  */
10
11
  const nextDistDir = process.env.NEXT_DIST_DIR || '.next';
11
12
 
13
+ /**
14
+ * Dependency root passed by `ptbk agents-server` when the bundled app is copied into a project cache.
15
+ */
16
+ const agentsServerNodeModulesPath = process.env.PTBK_AGENTS_SERVER_NODE_MODULES_PATH;
17
+
18
+ /**
19
+ * Whether the CLI-owned build should skip duplicate validation already covered by repository tests.
20
+ */
21
+ const isNextValidationIgnored = process.env.PTBK_AGENTS_SERVER_IGNORE_NEXT_VALIDATION === 'true';
22
+
23
+ /**
24
+ * Exact aliases for local generated Promptbook package entrypoints.
25
+ */
26
+ const promptbookLocalPackageAliases = createPromptbookLocalPackageAliases();
27
+
12
28
  /**
13
29
  * Map of next config.
14
30
  */
@@ -17,7 +33,13 @@ const nextConfig: NextConfig = {
17
33
  // <- TODO: [🐱‍🚀][🧠] How to properly build Next.js app, for both Vercel and Doceker?
18
34
 
19
35
  distDir: nextDistDir,
20
- serverExternalPackages: ['pg', '@napi-rs/canvas'],
36
+ eslint: {
37
+ ignoreDuringBuilds: isNextValidationIgnored,
38
+ },
39
+ serverExternalPackages: ['pg', '@napi-rs/canvas', 'playwright', 'playwright-core'],
40
+ typescript: {
41
+ ignoreBuildErrors: isNextValidationIgnored,
42
+ },
21
43
 
22
44
  experimental: {
23
45
  externalDir: true,
@@ -29,12 +51,25 @@ const nextConfig: NextConfig = {
29
51
 
30
52
  resolveAlias: {
31
53
  '@': path.resolve(__dirname),
32
- '@common': path.resolve(__dirname, '../common'),
33
- '@promptbook-local': path.resolve(__dirname, '../../src/_packages'),
54
+ '@common': path.resolve(__dirname, '../_common'),
55
+ ...promptbookLocalPackageAliases,
34
56
  },
35
57
  },
36
58
 
37
59
  webpack(config, { isServer }) {
60
+ config.resolve.alias = {
61
+ ...config.resolve.alias,
62
+ '@': path.resolve(__dirname),
63
+ '@common': path.resolve(__dirname, '../_common'),
64
+ ...promptbookLocalPackageAliases,
65
+ };
66
+
67
+ if (agentsServerNodeModulesPath) {
68
+ config.resolve.modules = Array.from(
69
+ new Set([...(config.resolve.modules || ['node_modules']), agentsServerNodeModulesPath]),
70
+ );
71
+ }
72
+
38
73
  // Exclude Node.js-only modules from client bundle
39
74
  if (!isServer) {
40
75
  config.resolve.fallback = {
@@ -81,3 +116,23 @@ const nextConfig: NextConfig = {
81
116
  };
82
117
 
83
118
  export default nextConfig;
119
+
120
+ /**
121
+ * Creates webpack/Turbopack aliases from imports like `@promptbook-local/core` to generated local sources.
122
+ */
123
+ function createPromptbookLocalPackageAliases(): Record<string, string> {
124
+ const packagesIndexPath = path.resolve(__dirname, '../../src/_packages');
125
+
126
+ try {
127
+ return Object.fromEntries(
128
+ readdirSync(packagesIndexPath)
129
+ .filter((filename) => filename.endsWith('.index.ts'))
130
+ .map((filename) => [
131
+ `@promptbook-local/${filename.replace(/\.index\.ts$/u, '')}`,
132
+ path.resolve(packagesIndexPath, filename),
133
+ ]),
134
+ );
135
+ } catch {
136
+ return {};
137
+ }
138
+ }
package/esm/index.es.js CHANGED
@@ -1,8 +1,8 @@
1
1
  import colors from 'colors';
2
2
  import commander, { Option } from 'commander';
3
3
  import _spaceTrim, { spaceTrim as spaceTrim$1 } from 'spacetrim';
4
- import { writeFile, stat, mkdir, readFile, readdir, unlink, appendFile, rm, rename, access, constants, watch, rmdir } from 'fs/promises';
5
- import { join, resolve, basename, relative, isAbsolute, dirname, extname } from 'path';
4
+ import { writeFile, stat, mkdir, readFile, rm, cp, lstat, symlink, readdir, unlink, appendFile, rename, access, constants, watch, rmdir } from 'fs/promises';
5
+ import { join, resolve, dirname, relative, basename, delimiter, isAbsolute, extname } from 'path';
6
6
  import { spawn } from 'child_process';
7
7
  import { createHash, randomBytes } from 'crypto';
8
8
  import * as fs from 'fs';
@@ -58,7 +58,7 @@ const BOOK_LANGUAGE_VERSION = '2.0.0';
58
58
  * @generated
59
59
  * @see https://github.com/webgptorg/promptbook
60
60
  */
61
- const PROMPTBOOK_ENGINE_VERSION = '0.112.0-87';
61
+ const PROMPTBOOK_ENGINE_VERSION = '0.112.0-89';
62
62
  /**
63
63
  * TODO: string_promptbook_version should be constrained to the all versions of Promptbook engine
64
64
  * Note: [💞] Ignore a discrepancy between file name and entity name
@@ -3102,6 +3102,31 @@ const AGENTS_SERVER_NEXT_BUILD_ID_FILENAME = 'BUILD_ID';
3102
3102
  * @private internal constant of `ptbk agents-server`
3103
3103
  */
3104
3104
  const DEFAULT_AGENTS_SERVER_NEXT_DIST_DIRECTORY_NAME = '.next';
3105
+ /**
3106
+ * Environment variable passed to the bundled Next app so webpack can resolve dependencies
3107
+ * installed beside `ptbk` even when the app sources are materialized into a project cache.
3108
+ *
3109
+ * @private internal constant of `ptbk agents-server`
3110
+ */
3111
+ const PTBK_AGENTS_SERVER_NODE_MODULES_PATH_ENV = 'PTBK_AGENTS_SERVER_NODE_MODULES_PATH';
3112
+ /**
3113
+ * Environment variable used only by the CLI-owned production build.
3114
+ *
3115
+ * @private internal constant of `ptbk agents-server`
3116
+ */
3117
+ const PTBK_AGENTS_SERVER_IGNORE_NEXT_VALIDATION_ENV = 'PTBK_AGENTS_SERVER_IGNORE_NEXT_VALIDATION';
3118
+ /**
3119
+ * Runtime copy metadata filename stored in the project-local materialized app root.
3120
+ *
3121
+ * @private internal constant of `ptbk agents-server`
3122
+ */
3123
+ const AGENTS_SERVER_MATERIALIZED_RUNTIME_CACHE_FILENAME = '.ptbk-agents-server-runtime-cache.json';
3124
+ /**
3125
+ * Directory segment used for Node package installs.
3126
+ *
3127
+ * @private internal constant of `ptbk agents-server`
3128
+ */
3129
+ const NODE_MODULES_DIRECTORY_NAME = 'node_modules';
3105
3130
  /**
3106
3131
  * Runtime paths copied into the CLI package and used by the Agents Server production build.
3107
3132
  *
@@ -3146,26 +3171,33 @@ const AGENTS_SERVER_BUILD_INPUT_TEST_FILE_PATTERN = /\.(?:spec|test)\.[jt]sx?$/i
3146
3171
  */
3147
3172
  async function ensureAgentsServerBuild(options = {}) {
3148
3173
  var _a, _b, _c, _d;
3149
- const appPath = (_a = options.appPath) !== null && _a !== void 0 ? _a : (await resolveAgentsServerAppPath());
3150
- const environment = (_b = options.environment) !== null && _b !== void 0 ? _b : process.env;
3174
+ const environment = (_a = options.environment) !== null && _a !== void 0 ? _a : process.env;
3151
3175
  const nextCliPath = resolveNextCliPath();
3176
+ const nodeModulesPath = resolveNodeModulesPath(nextCliPath);
3177
+ const appPath = await resolveAgentsServerBuildAppPath({
3178
+ sourceAppPath: (_b = options.appPath) !== null && _b !== void 0 ? _b : (await resolveAgentsServerAppPath()),
3179
+ nodeModulesPath,
3180
+ });
3181
+ const buildEnvironment = createAgentsServerRuntimeEnvironment(environment, nodeModulesPath, {
3182
+ isNextValidationIgnored: isAgentsServerAppPathMaterialized(appPath),
3183
+ });
3152
3184
  if (!options.isBuildForced &&
3153
3185
  (await isAgentsServerBuildCacheCurrent({
3154
3186
  appPath,
3155
- environment,
3187
+ environment: buildEnvironment,
3156
3188
  }))) {
3157
3189
  (_c = options.onBuildEvent) === null || _c === void 0 ? void 0 : _c.call(options, 'Using the cached Agents Server Next app build.');
3158
- return { appPath, nextCliPath };
3190
+ return { appPath, nextCliPath, nodeModulesPath };
3159
3191
  }
3160
3192
  (_d = options.onBuildEvent) === null || _d === void 0 ? void 0 : _d.call(options, 'Building the Agents Server Next app.');
3161
3193
  await runNextBuild({
3162
3194
  appPath,
3163
- environment,
3195
+ environment: buildEnvironment,
3164
3196
  nextCliPath,
3165
3197
  onBuildOutput: options.onBuildOutput,
3166
3198
  });
3167
- await writeAgentsServerBuildCache({ appPath, environment });
3168
- return { appPath, nextCliPath };
3199
+ await writeAgentsServerBuildCache({ appPath, environment: buildEnvironment });
3200
+ return { appPath, nextCliPath, nodeModulesPath };
3169
3201
  }
3170
3202
  /**
3171
3203
  * Returns true when the production build marker and source fingerprint still match.
@@ -3221,6 +3253,165 @@ async function resolveAgentsServerAppPath() {
3221
3253
  ${candidates.map((candidate) => `- \`${candidate}\``).join('\n')}
3222
3254
  `));
3223
3255
  }
3256
+ /**
3257
+ * Adds dependency-resolution environment required by the materialized Agents Server runtime.
3258
+ *
3259
+ * @private internal utility of `ptbk agents-server`
3260
+ */
3261
+ function createAgentsServerRuntimeEnvironment(environment, nodeModulesPath, options = {}) {
3262
+ return {
3263
+ ...environment,
3264
+ NODE_PATH: mergeNodePath(nodeModulesPath, environment.NODE_PATH),
3265
+ [PTBK_AGENTS_SERVER_NODE_MODULES_PATH_ENV]: nodeModulesPath,
3266
+ ...(options.isNextValidationIgnored
3267
+ ? {
3268
+ [PTBK_AGENTS_SERVER_IGNORE_NEXT_VALIDATION_ENV]: 'true',
3269
+ }
3270
+ : {}),
3271
+ };
3272
+ }
3273
+ /**
3274
+ * Uses the source checkout app directly, but copies npm-packaged app sources out of `node_modules`.
3275
+ *
3276
+ * @private internal utility of `ptbk agents-server`
3277
+ */
3278
+ async function resolveAgentsServerBuildAppPath(options) {
3279
+ if (!isPathInsideNodeModules(options.sourceAppPath)) {
3280
+ return options.sourceAppPath;
3281
+ }
3282
+ const sourceRuntimeRootPath = resolve(options.sourceAppPath, '..', '..');
3283
+ const materializedRuntimeRootPath = resolvePromptbookTemporaryPath(process.cwd(), 'agents-server', 'runtime');
3284
+ await synchronizeMaterializedAgentsServerRuntime({
3285
+ materializedRuntimeRootPath,
3286
+ nodeModulesPath: options.nodeModulesPath,
3287
+ sourceAppPath: options.sourceAppPath,
3288
+ sourceRuntimeRootPath,
3289
+ });
3290
+ return join(materializedRuntimeRootPath, 'apps', 'agents-server');
3291
+ }
3292
+ /**
3293
+ * Copies the bundled runtime into the launch project when the original app lives under `node_modules`.
3294
+ */
3295
+ async function synchronizeMaterializedAgentsServerRuntime(options) {
3296
+ const sourceFingerprint = await createAgentsServerBuildSourceFingerprint(options.sourceAppPath);
3297
+ const runtimeCache = await readAgentsServerMaterializedRuntimeCache(options.materializedRuntimeRootPath);
3298
+ const isRuntimeCurrent = (runtimeCache === null || runtimeCache === void 0 ? void 0 : runtimeCache.version) === AGENTS_SERVER_BUILD_CACHE_VERSION &&
3299
+ runtimeCache.sourceFingerprint === sourceFingerprint &&
3300
+ (await isAgentsServerAppPath(join(options.materializedRuntimeRootPath, 'apps', 'agents-server')));
3301
+ if (!isRuntimeCurrent) {
3302
+ await rm(options.materializedRuntimeRootPath, { recursive: true, force: true });
3303
+ await mkdir(options.materializedRuntimeRootPath, { recursive: true });
3304
+ for (const relativeInputPath of AGENTS_SERVER_BUILD_INPUT_RELATIVE_PATHS) {
3305
+ await copyAgentsServerRuntimePath({
3306
+ destinationPath: join(options.materializedRuntimeRootPath, relativeInputPath),
3307
+ sourcePath: join(options.sourceRuntimeRootPath, relativeInputPath),
3308
+ });
3309
+ }
3310
+ await assertMaterializedAgentsServerAppExists(options.materializedRuntimeRootPath);
3311
+ await writeAgentsServerMaterializedRuntimeCache(options.materializedRuntimeRootPath, sourceFingerprint);
3312
+ }
3313
+ await ensureMaterializedRuntimeNodeModulesLink({
3314
+ materializedRuntimeRootPath: options.materializedRuntimeRootPath,
3315
+ nodeModulesPath: options.nodeModulesPath,
3316
+ });
3317
+ }
3318
+ /**
3319
+ * Verifies that the materialized runtime contains a usable Next app before caching it.
3320
+ */
3321
+ async function assertMaterializedAgentsServerAppExists(materializedRuntimeRootPath) {
3322
+ const materializedAppPath = join(materializedRuntimeRootPath, 'apps', 'agents-server');
3323
+ if (await isAgentsServerAppPath(materializedAppPath)) {
3324
+ return;
3325
+ }
3326
+ throw new NotAllowed(spaceTrim$1(`
3327
+ Cannot prepare the bundled Agents Server runtime.
3328
+
3329
+ The materialized app path is missing required files:
3330
+ \`${materializedAppPath}\`
3331
+ `));
3332
+ }
3333
+ /**
3334
+ * Copies one source path into the materialized runtime, skipping generated or private files.
3335
+ */
3336
+ async function copyAgentsServerRuntimePath(options) {
3337
+ let sourceStats;
3338
+ try {
3339
+ sourceStats = await stat(options.sourcePath);
3340
+ }
3341
+ catch (_a) {
3342
+ return;
3343
+ }
3344
+ await mkdir(dirname(options.destinationPath), { recursive: true });
3345
+ if (sourceStats.isDirectory()) {
3346
+ await cp(options.sourcePath, options.destinationPath, {
3347
+ recursive: true,
3348
+ filter: (sourcePath) => shouldCopyAgentsServerRuntimePath(sourcePath, options.sourcePath),
3349
+ });
3350
+ return;
3351
+ }
3352
+ if (sourceStats.isFile() && shouldCopyAgentsServerRuntimePath(options.sourcePath, dirname(options.sourcePath))) {
3353
+ await cp(options.sourcePath, options.destinationPath);
3354
+ }
3355
+ }
3356
+ /**
3357
+ * Excludes build artifacts, dependency folders, private env files, and test sources.
3358
+ */
3359
+ function shouldCopyAgentsServerRuntimePath(sourcePath, sourceRootPath) {
3360
+ const sourceRelativePath = relative(sourceRootPath, sourcePath).replace(/\\/gu, '/');
3361
+ const sourcePathSegments = sourceRelativePath.split('/').filter(Boolean);
3362
+ const sourceBasename = basename(sourcePath);
3363
+ if (sourcePathSegments.some((sourcePathSegment) => AGENTS_SERVER_BUILD_INPUT_EXCLUDED_DIRECTORY_NAMES.has(sourcePathSegment))) {
3364
+ return false;
3365
+ }
3366
+ if (sourceBasename.startsWith('.env')) {
3367
+ return false;
3368
+ }
3369
+ return !AGENTS_SERVER_BUILD_INPUT_TEST_FILE_PATTERN.test(sourceBasename);
3370
+ }
3371
+ /**
3372
+ * Links the materialized runtime back to the package dependency tree used by the installed CLI.
3373
+ */
3374
+ async function ensureMaterializedRuntimeNodeModulesLink(options) {
3375
+ const nodeModulesLinkPath = join(options.materializedRuntimeRootPath, NODE_MODULES_DIRECTORY_NAME);
3376
+ try {
3377
+ const existingLinkStats = await lstat(nodeModulesLinkPath);
3378
+ await rm(nodeModulesLinkPath, {
3379
+ force: true,
3380
+ recursive: existingLinkStats.isDirectory() && !existingLinkStats.isSymbolicLink(),
3381
+ });
3382
+ }
3383
+ catch (_a) {
3384
+ // The link does not exist yet.
3385
+ }
3386
+ await symlink(options.nodeModulesPath, nodeModulesLinkPath, process.platform === 'win32' ? 'junction' : 'dir');
3387
+ }
3388
+ /**
3389
+ * Reads and validates materialized runtime metadata.
3390
+ */
3391
+ async function readAgentsServerMaterializedRuntimeCache(materializedRuntimeRootPath) {
3392
+ try {
3393
+ const serializedRuntimeCache = await readFile(join(materializedRuntimeRootPath, AGENTS_SERVER_MATERIALIZED_RUNTIME_CACHE_FILENAME), 'utf-8');
3394
+ const runtimeCache = JSON.parse(serializedRuntimeCache);
3395
+ if (runtimeCache.version !== AGENTS_SERVER_BUILD_CACHE_VERSION ||
3396
+ typeof runtimeCache.sourceFingerprint !== 'string') {
3397
+ return undefined;
3398
+ }
3399
+ return runtimeCache;
3400
+ }
3401
+ catch (_a) {
3402
+ return undefined;
3403
+ }
3404
+ }
3405
+ /**
3406
+ * Persists the source fingerprint used for the materialized runtime copy.
3407
+ */
3408
+ async function writeAgentsServerMaterializedRuntimeCache(materializedRuntimeRootPath, sourceFingerprint) {
3409
+ const runtimeCache = {
3410
+ version: AGENTS_SERVER_BUILD_CACHE_VERSION,
3411
+ sourceFingerprint,
3412
+ };
3413
+ await writeFile(join(materializedRuntimeRootPath, AGENTS_SERVER_MATERIALIZED_RUNTIME_CACHE_FILENAME), `${JSON.stringify(runtimeCache, null, 4)}\n`, 'utf-8');
3414
+ }
3224
3415
  /**
3225
3416
  * Runs the finite Next production build used by local Agents Server commands.
3226
3417
  */
@@ -3345,6 +3536,41 @@ function resolveAgentsServerBuildOutputPath(options) {
3345
3536
  var _a;
3346
3537
  return resolve(options.appPath, ((_a = options.environment) === null || _a === void 0 ? void 0 : _a.NEXT_DIST_DIR) || DEFAULT_AGENTS_SERVER_NEXT_DIST_DIRECTORY_NAME);
3347
3538
  }
3539
+ /**
3540
+ * Returns true when the app path points at the project-local materialized runtime.
3541
+ */
3542
+ function isAgentsServerAppPathMaterialized(appPath) {
3543
+ const normalizedAppPath = appPath.replace(/\\/gu, '/');
3544
+ const normalizedMaterializedRuntimePath = resolvePromptbookTemporaryPath(process.cwd(), 'agents-server', 'runtime').replace(/\\/gu, '/');
3545
+ return normalizedAppPath.includes(normalizedMaterializedRuntimePath);
3546
+ }
3547
+ /**
3548
+ * Returns true when one path is nested below a `node_modules` segment.
3549
+ */
3550
+ function isPathInsideNodeModules(path) {
3551
+ return path.split(/[\\/]+/u).includes(NODE_MODULES_DIRECTORY_NAME);
3552
+ }
3553
+ /**
3554
+ * Resolves the dependency root that contains the installed Next CLI.
3555
+ */
3556
+ function resolveNodeModulesPath(nextCliPath) {
3557
+ const normalizedNextCliPath = nextCliPath.replace(/\\/gu, '/');
3558
+ const marker = `/${NODE_MODULES_DIRECTORY_NAME}/next/`;
3559
+ const markerIndex = normalizedNextCliPath.lastIndexOf(marker);
3560
+ if (markerIndex === -1) {
3561
+ return resolve(nextCliPath, '..', '..', '..');
3562
+ }
3563
+ return normalizedNextCliPath.slice(0, markerIndex + marker.length - '/next/'.length);
3564
+ }
3565
+ /**
3566
+ * Prepends one dependency root to `NODE_PATH` while preserving any existing value.
3567
+ */
3568
+ function mergeNodePath(nodeModulesPath, nodePath) {
3569
+ if (!nodePath) {
3570
+ return nodeModulesPath;
3571
+ }
3572
+ return `${nodeModulesPath}${delimiter}${nodePath}`;
3573
+ }
3348
3574
  /**
3349
3575
  * Returns true when one path exists as a regular file.
3350
3576
  */
@@ -35911,7 +36137,7 @@ async function startAgentsServer(options) {
35911
36137
  };
35912
36138
  process.once('exit', processExitHandler);
35913
36139
  try {
35914
- const { nextCliPath } = await ensureAgentsServerBuild({
36140
+ const buildArtifacts = await ensureAgentsServerBuild({
35915
36141
  appPath: runtimePaths.appPath,
35916
36142
  environment: childEnvironment,
35917
36143
  isBuildForced: options.isBuildForced,
@@ -35931,17 +36157,22 @@ async function startAgentsServer(options) {
35931
36157
  });
35932
36158
  },
35933
36159
  });
36160
+ const runtimeChildEnvironment = createAgentsServerRuntimeEnvironment(childEnvironment, buildArtifacts.nodeModulesPath);
36161
+ const builtRuntimePaths = {
36162
+ ...runtimePaths,
36163
+ appPath: buildArtifacts.appPath,
36164
+ };
35934
36165
  nextServerProcess = startNextServer({
35935
- nextCliPath,
36166
+ nextCliPath: buildArtifacts.nextCliPath,
35936
36167
  options,
35937
- runtimePaths,
35938
- childEnvironment,
36168
+ runtimePaths: builtRuntimePaths,
36169
+ childEnvironment: runtimeChildEnvironment,
35939
36170
  logStreams,
35940
36171
  state,
35941
36172
  });
35942
36173
  stopUserChatJobWorkerPump = startUserChatJobWorkerPump({
35943
36174
  port: options.port,
35944
- environment: childEnvironment,
36175
+ environment: runtimeChildEnvironment,
35945
36176
  logStreams,
35946
36177
  state,
35947
36178
  });
@@ -35967,6 +36198,8 @@ async function startAgentsServer(options) {
35967
36198
  }
35968
36199
  /**
35969
36200
  * Loads launch-directory `.env` values without overriding explicit process environment.
36201
+ *
36202
+ * @private internal utility of `ptbk agents-server`
35970
36203
  */
35971
36204
  function loadAgentsServerProjectEnvironment(launchWorkingDirectory) {
35972
36205
  dotenv.config({ path: join(launchWorkingDirectory, AGENTS_SERVER_PROJECT_ENV_FILE_NAME) });
@@ -36324,6 +36557,7 @@ function $initializeAgentsServerBuildCommand(program) {
36324
36557
  command.description('Build the Agents Server Next app for later local startup');
36325
36558
  command.action(handleActionErrors(async () => {
36326
36559
  console.info(colors.gray('Building Promptbook Agents Server.'));
36560
+ loadAgentsServerProjectEnvironment(process.cwd());
36327
36561
  await ensureAgentsServerBuild({ isBuildForced: true });
36328
36562
  }));
36329
36563
  }