@promptbook/cli 0.112.0-41 → 0.112.0-42

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 (21) hide show
  1. package/esm/apps/agents-server/src/database/acquireMigrationExecutionLock.d.ts +12 -1
  2. package/esm/apps/agents-server/src/database/runDatabaseMigrations.d.ts +9 -0
  3. package/esm/index.es.js +189 -29
  4. package/esm/index.es.js.map +1 -1
  5. package/esm/scripts/verify-prompts/verify-prompts.d.ts +23 -2
  6. package/esm/src/cli/cli-commands/coder/verify.test.d.ts +1 -0
  7. package/esm/src/collection/agent-collection/constructors/agent-collection-in-supabase/AgentCollectionInSupabase.d.ts +2 -14
  8. package/esm/src/collection/agent-collection/constructors/agent-collection-in-supabase/createAgentPersistenceRecords.d.ts +40 -0
  9. package/esm/src/collection/agent-collection/constructors/agent-collection-in-supabase/createAgentPersistenceRecords.test.d.ts +1 -0
  10. package/esm/src/version.d.ts +1 -1
  11. package/package.json +1 -1
  12. package/umd/apps/agents-server/src/database/acquireMigrationExecutionLock.d.ts +12 -1
  13. package/umd/apps/agents-server/src/database/runDatabaseMigrations.d.ts +9 -0
  14. package/umd/index.umd.js +189 -29
  15. package/umd/index.umd.js.map +1 -1
  16. package/umd/scripts/verify-prompts/verify-prompts.d.ts +23 -2
  17. package/umd/src/cli/cli-commands/coder/verify.test.d.ts +1 -0
  18. package/umd/src/collection/agent-collection/constructors/agent-collection-in-supabase/AgentCollectionInSupabase.d.ts +2 -14
  19. package/umd/src/collection/agent-collection/constructors/agent-collection-in-supabase/createAgentPersistenceRecords.d.ts +40 -0
  20. package/umd/src/collection/agent-collection/constructors/agent-collection-in-supabase/createAgentPersistenceRecords.test.d.ts +1 -0
  21. package/umd/src/version.d.ts +1 -1
@@ -1,14 +1,25 @@
1
1
  import type { Client } from 'pg';
2
2
  import type { DatabaseMigrationLogger } from './runDatabaseMigrations';
3
+ /**
4
+ * Lock-acquisition strategies used by the migration runner.
5
+ *
6
+ * `wait` is appropriate for explicit/manual migration flows where the caller expects one serialized run.
7
+ * `skip` is appropriate for background startup checks where waiting would block request handling.
8
+ *
9
+ * @private function of runDatabaseMigrations
10
+ */
11
+ export type DatabaseMigrationExecutionLockMode = 'wait' | 'skip';
3
12
  /**
4
13
  * Acquires advisory lock preventing concurrent migration execution across processes.
5
14
  *
6
15
  * @param client Connected PostgreSQL client.
7
16
  * @param logger Logger used for progress output.
17
+ * @param mode Whether the caller should block for the lock or skip when it is already held.
18
+ * @returns `true` when the lock was acquired and migrations may proceed.
8
19
  *
9
20
  * @private function of runDatabaseMigrations
10
21
  */
11
- export declare function acquireMigrationExecutionLock(client: Client, logger: DatabaseMigrationLogger): Promise<void>;
22
+ export declare function acquireMigrationExecutionLock(client: Client, logger: DatabaseMigrationLogger, mode?: DatabaseMigrationExecutionLockMode): Promise<boolean>;
12
23
  /**
13
24
  * Releases advisory lock used for migration execution.
14
25
  *
@@ -1,4 +1,5 @@
1
1
  import { type ServerRecord } from '../utils/serverRegistry';
2
+ import { type DatabaseMigrationExecutionLockMode } from './acquireMigrationExecutionLock';
2
3
  /**
3
4
  * Allowed values describing who applied a migration record.
4
5
  */
@@ -67,6 +68,10 @@ export type RunDatabaseMigrationsOptions = {
67
68
  * If true, prints every SQL migration body before execution.
68
69
  */
69
70
  readonly logSqlStatements?: boolean;
71
+ /**
72
+ * Whether migration execution should wait for the advisory lock or skip when another process already owns it.
73
+ */
74
+ readonly executionLockMode?: DatabaseMigrationExecutionLockMode;
70
75
  };
71
76
  /**
72
77
  * Result of one prefix migration run.
@@ -97,6 +102,10 @@ export type RunDatabaseMigrationsResult = {
97
102
  * Per-prefix summary.
98
103
  */
99
104
  readonly perPrefix: ReadonlyArray<DatabaseMigrationPrefixResult>;
105
+ /**
106
+ * Whether migration execution was skipped because another process already held the advisory lock.
107
+ */
108
+ readonly isSkippedDueToActiveMigrationLock: boolean;
100
109
  };
101
110
  /**
102
111
  * Reads migration runtime configuration from environment variables and `_Server`.
package/esm/index.es.js CHANGED
@@ -57,7 +57,7 @@ const BOOK_LANGUAGE_VERSION = '2.0.0';
57
57
  * @generated
58
58
  * @see https://github.com/webgptorg/promptbook
59
59
  */
60
- const PROMPTBOOK_ENGINE_VERSION = '0.112.0-41';
60
+ const PROMPTBOOK_ENGINE_VERSION = '0.112.0-42';
61
61
  /**
62
62
  * TODO: string_promptbook_version should be constrained to the all versions of Promptbook engine
63
63
  * Note: [💞] Ignore a discrepancy between file name and entity name
@@ -3091,14 +3091,16 @@ function $initializeCoderVerifyCommand(program) {
3091
3091
  - Archives verified prompt files to prompts/done/ directory
3092
3092
  - Auto-appends repair prompts for incomplete work
3093
3093
  - Processes files with all-done prompts first
3094
+ - Supports ignoring matching prompt candidates for one verification run
3094
3095
  `));
3095
3096
  command.option('--reverse', 'Process prompt files in reverse order', false);
3097
+ command.option('--ignore <candidate-text>', 'Ignore prompt files whose filename or first prompt line contains the given text (repeatable)', collectStringOption, []);
3096
3098
  command.action(handleActionErrors(async (cliOptions) => {
3097
- const { reverse } = cliOptions;
3099
+ const { reverse, ignore } = cliOptions;
3098
3100
  // Note: Import the main function dynamically to avoid loading heavy dependencies until needed
3099
3101
  const { verifyPrompts } = await Promise.resolve().then(function () { return verifyPrompts$1; });
3100
3102
  try {
3101
- await verifyPrompts(reverse);
3103
+ await verifyPrompts({ reverse, ignore });
3102
3104
  }
3103
3105
  catch (error) {
3104
3106
  console.error(colors.bgRed('Prompt verification failed:'), error);
@@ -3107,6 +3109,14 @@ function $initializeCoderVerifyCommand(program) {
3107
3109
  return process.exit(0);
3108
3110
  }));
3109
3111
  }
3112
+ /**
3113
+ * Collects repeatable string options from Commander.
3114
+ *
3115
+ * @private internal utility of `coder verify` command
3116
+ */
3117
+ function collectStringOption(value, previousValues) {
3118
+ return [...previousValues, value];
3119
+ }
3110
3120
  // Note: [🟡] Code for CLI command [verify](src/cli/cli-commands/coder/verify.ts) should never be published outside of `@promptbook/cli`
3111
3121
  // Note: [💞] Ignore a discrepancy between file name and entity name
3112
3122
 
@@ -45389,6 +45399,7 @@ function normalizePathForLogs(value) {
45389
45399
  return value.split('\\').join('/');
45390
45400
  }
45391
45401
 
45402
+ // cspell:ignore hashtext
45392
45403
  /**
45393
45404
  * Cross-process advisory lock key guarding migration execution.
45394
45405
  *
@@ -45400,13 +45411,27 @@ const DATABASE_MIGRATION_LOCK_KEY = 'promptbook_agents_server_migrations';
45400
45411
  *
45401
45412
  * @param client Connected PostgreSQL client.
45402
45413
  * @param logger Logger used for progress output.
45414
+ * @param mode Whether the caller should block for the lock or skip when it is already held.
45415
+ * @returns `true` when the lock was acquired and migrations may proceed.
45403
45416
  *
45404
45417
  * @private function of runDatabaseMigrations
45405
45418
  */
45406
- async function acquireMigrationExecutionLock(client, logger) {
45419
+ async function acquireMigrationExecutionLock(client, logger, mode = 'wait') {
45420
+ var _a;
45421
+ if (mode === 'skip') {
45422
+ logger.info('🔒 Checking migration lock without waiting');
45423
+ const { rows } = await client.query('SELECT pg_try_advisory_lock(hashtext($1)) AS "isMigrationLockAcquired";', [DATABASE_MIGRATION_LOCK_KEY]);
45424
+ if (!((_a = rows[0]) === null || _a === void 0 ? void 0 : _a.isMigrationLockAcquired)) {
45425
+ logger.info('⏭️ Migration lock is already held by another process. Skipping this migration attempt.');
45426
+ return false;
45427
+ }
45428
+ logger.info('🔒 Migration lock acquired');
45429
+ return true;
45430
+ }
45407
45431
  logger.info('🔒 Waiting for migration lock');
45408
45432
  await client.query('SELECT pg_advisory_lock(hashtext($1));', [DATABASE_MIGRATION_LOCK_KEY]);
45409
45433
  logger.info('🔒 Migration lock acquired');
45434
+ return true;
45410
45435
  }
45411
45436
  /**
45412
45437
  * Releases advisory lock used for migration execution.
@@ -45930,23 +45955,33 @@ async function resolveDatabaseMigrationRuntimeConfiguration(logger = console) {
45930
45955
  * @returns Aggregated migration summary.
45931
45956
  */
45932
45957
  async function runDatabaseMigrations(options) {
45933
- var _a, _b, _c, _d;
45958
+ var _a, _b, _c, _d, _e;
45934
45959
  const logger = (_a = options.logger) !== null && _a !== void 0 ? _a : console;
45935
45960
  const selectedPrefixes = selectPrefixesForMigration(options.prefixes, (_b = options.registeredServers) !== null && _b !== void 0 ? _b : [], options.onlyTargets);
45936
45961
  const migrationsDirectory = (_c = options.migrationsDirectory) !== null && _c !== void 0 ? _c : (await resolveMigrationsDirectory());
45937
45962
  const migrationFiles = await readMigrationFiles(migrationsDirectory);
45963
+ const totalMigrationFiles = migrationFiles.length;
45938
45964
  const perPrefix = [];
45939
- logger.info(`📂 Found ${migrationFiles.length} migration files`);
45965
+ logger.info(`📂 Found ${totalMigrationFiles} migration files`);
45940
45966
  logger.info(`📋 Found ${selectedPrefixes.length} prefixes to migrate: ${selectedPrefixes
45941
45967
  .map((prefix) => prefix || '<default>')
45942
45968
  .join(', ')}`);
45943
45969
  const client = await createPostgresClient(options.connectionString);
45944
45970
  let hasExecutionLock = false;
45971
+ let isSkippedDueToActiveMigrationLock = false;
45945
45972
  try {
45946
45973
  await client.connect();
45947
45974
  logger.info('🔌 Connected to database');
45948
- await acquireMigrationExecutionLock(client, logger);
45949
- hasExecutionLock = true;
45975
+ hasExecutionLock = await acquireMigrationExecutionLock(client, logger, (_d = options.executionLockMode) !== null && _d !== void 0 ? _d : 'wait');
45976
+ if (!hasExecutionLock) {
45977
+ isSkippedDueToActiveMigrationLock = true;
45978
+ return {
45979
+ processedPrefixes: [],
45980
+ totalMigrationFiles,
45981
+ perPrefix,
45982
+ isSkippedDueToActiveMigrationLock,
45983
+ };
45984
+ }
45950
45985
  for (const prefix of selectedPrefixes) {
45951
45986
  logger.info(`\n🏗️ Migrating prefix: "${prefix}"`);
45952
45987
  const appliedCount = await migratePrefix({
@@ -45957,7 +45992,7 @@ async function runDatabaseMigrations(options) {
45957
45992
  logger,
45958
45993
  migrationFiles,
45959
45994
  migrationsDirectory,
45960
- logSqlStatements: (_d = options.logSqlStatements) !== null && _d !== void 0 ? _d : false,
45995
+ logSqlStatements: (_e = options.logSqlStatements) !== null && _e !== void 0 ? _e : false,
45961
45996
  });
45962
45997
  perPrefix.push({ prefix, appliedCount });
45963
45998
  }
@@ -45970,8 +46005,9 @@ async function runDatabaseMigrations(options) {
45970
46005
  }
45971
46006
  return {
45972
46007
  processedPrefixes: selectedPrefixes,
45973
- totalMigrationFiles: migrationFiles.length,
46008
+ totalMigrationFiles,
45974
46009
  perPrefix,
46010
+ isSkippedDueToActiveMigrationLock,
45975
46011
  };
45976
46012
  }
45977
46013
  /**
@@ -48159,24 +48195,26 @@ const SNIPPET_CHAR_LIMIT = 900;
48159
48195
  */
48160
48196
  const MAX_PENDING_FILE_NAMES = 8;
48161
48197
  /**
48162
- * Parse command-line arguments
48198
+ * Default verification options parsed from the current process arguments.
48163
48199
  */
48164
- const REVERSE_ORDER = process.argv.includes('--reverse');
48200
+ const DEFAULT_VERIFY_PROMPTS_OPTIONS = normalizeVerifyPromptsOptions(parseVerifyPromptsCliOptions(process.argv.slice(2)));
48165
48201
  /**
48166
48202
  * Starts the verification loop and exits when no `[ ]` prompts remain.
48167
48203
  *
48168
- * @param reverse - Process files in reverse order
48169
- *
48170
48204
  * @public exported from `@promptbook/cli`
48171
48205
  */
48172
- async function verifyPrompts(reverse = REVERSE_ORDER) {
48206
+ async function verifyPrompts(options = DEFAULT_VERIFY_PROMPTS_OPTIONS) {
48207
+ const normalizedOptions = normalizeVerifyPromptsOptions(options);
48173
48208
  console.info(colors.cyan.bold('📋 Prompt verification helper'));
48174
- if (reverse) {
48209
+ if (normalizedOptions.reverse) {
48175
48210
  console.info(colors.gray('Processing files in reverse order'));
48176
48211
  }
48177
- const initialFiles = await loadPromptFiles(PROMPTS_DIR);
48178
- if (reverse) {
48179
- initialFiles.reverse();
48212
+ if (normalizedOptions.ignore.length > 0) {
48213
+ console.info(colors.gray(`Ignoring candidates matching: ${normalizedOptions.ignore.join(', ')}`));
48214
+ }
48215
+ const { promptFiles: initialFiles, ignoredPromptFiles } = await loadPromptFilesForVerification(normalizedOptions);
48216
+ if (ignoredPromptFiles.length > 0) {
48217
+ console.info(colors.gray(`Ignored ${ignoredPromptFiles.length} prompt file(s) for this run.`));
48180
48218
  }
48181
48219
  displayTopLevelFileList(initialFiles);
48182
48220
  await prepareArchiveDirectory();
@@ -48191,10 +48229,7 @@ async function verifyPrompts(reverse = REVERSE_ORDER) {
48191
48229
  if (wasSkipped) {
48192
48230
  skippedFiles.add(fileWithAllDone.path);
48193
48231
  }
48194
- promptFiles = await loadPromptFiles(PROMPTS_DIR);
48195
- if (REVERSE_ORDER) {
48196
- promptFiles.reverse();
48197
- }
48232
+ promptFiles = (await loadPromptFilesForVerification(normalizedOptions)).promptFiles;
48198
48233
  continue;
48199
48234
  }
48200
48235
  // Second priority: process todo prompts
@@ -48204,11 +48239,56 @@ async function verifyPrompts(reverse = REVERSE_ORDER) {
48204
48239
  break;
48205
48240
  }
48206
48241
  await resolvePrompt(nextPrompt);
48207
- promptFiles = await loadPromptFiles(PROMPTS_DIR);
48208
- if (REVERSE_ORDER) {
48209
- promptFiles.reverse();
48242
+ promptFiles = (await loadPromptFilesForVerification(normalizedOptions)).promptFiles;
48243
+ }
48244
+ }
48245
+ /**
48246
+ * Parses supported command-line arguments for the standalone verification script.
48247
+ */
48248
+ function parseVerifyPromptsCliOptions(args) {
48249
+ return {
48250
+ reverse: args.includes('--reverse'),
48251
+ ignore: readRepeatableStringOption(args, '--ignore'),
48252
+ };
48253
+ }
48254
+ /**
48255
+ * Loads prompt files and applies ordering plus ignore filters for one verification pass.
48256
+ */
48257
+ async function loadPromptFilesForVerification(options) {
48258
+ const loadedPromptFiles = await loadPromptFiles(PROMPTS_DIR);
48259
+ const { promptFiles, ignoredPromptFiles } = partitionPromptFilesByIgnore(loadedPromptFiles, options.ignore);
48260
+ if (options.reverse) {
48261
+ promptFiles.reverse();
48262
+ }
48263
+ return { promptFiles, ignoredPromptFiles };
48264
+ }
48265
+ /**
48266
+ * Splits prompt files into files that should be verified now and files ignored for this run.
48267
+ *
48268
+ * @public exported from `@promptbook/cli`
48269
+ */
48270
+ function partitionPromptFilesByIgnore(promptFiles, ignoreValues) {
48271
+ const normalizedIgnoreValues = normalizeIgnoreValues(ignoreValues);
48272
+ if (normalizedIgnoreValues.length === 0) {
48273
+ return {
48274
+ promptFiles: [...promptFiles],
48275
+ ignoredPromptFiles: [],
48276
+ };
48277
+ }
48278
+ const promptFilesToVerify = [];
48279
+ const ignoredPromptFiles = [];
48280
+ for (const promptFile of promptFiles) {
48281
+ if (matchesIgnoredPromptFile(promptFile, normalizedIgnoreValues)) {
48282
+ ignoredPromptFiles.push(promptFile);
48283
+ }
48284
+ else {
48285
+ promptFilesToVerify.push(promptFile);
48210
48286
  }
48211
48287
  }
48288
+ return {
48289
+ promptFiles: promptFilesToVerify,
48290
+ ignoredPromptFiles,
48291
+ };
48212
48292
  }
48213
48293
  /**
48214
48294
  * Ensures the destination directory for completed prompts exists.
@@ -48216,13 +48296,92 @@ async function verifyPrompts(reverse = REVERSE_ORDER) {
48216
48296
  async function prepareArchiveDirectory() {
48217
48297
  await mkdir(DONE_PROMPTS_DIR, { recursive: true });
48218
48298
  }
48299
+ /**
48300
+ * Normalizes verification options so the rest of the flow can assume stable defaults.
48301
+ */
48302
+ function normalizeVerifyPromptsOptions(options) {
48303
+ var _a, _b;
48304
+ return {
48305
+ reverse: (_a = options.reverse) !== null && _a !== void 0 ? _a : false,
48306
+ ignore: normalizeIgnoreValues((_b = options.ignore) !== null && _b !== void 0 ? _b : []),
48307
+ };
48308
+ }
48309
+ /**
48310
+ * Normalizes ignore values and removes empty or duplicate entries case-insensitively.
48311
+ */
48312
+ function normalizeIgnoreValues(ignoreValues) {
48313
+ const normalizedIgnoreValues = [];
48314
+ const seenIgnoreValues = new Set();
48315
+ for (const ignoreValue of ignoreValues) {
48316
+ const trimmedIgnoreValue = ignoreValue.trim();
48317
+ if (!trimmedIgnoreValue) {
48318
+ continue;
48319
+ }
48320
+ const lowerCasedIgnoreValue = trimmedIgnoreValue.toLowerCase();
48321
+ if (seenIgnoreValues.has(lowerCasedIgnoreValue)) {
48322
+ continue;
48323
+ }
48324
+ seenIgnoreValues.add(lowerCasedIgnoreValue);
48325
+ normalizedIgnoreValues.push(trimmedIgnoreValue);
48326
+ }
48327
+ return normalizedIgnoreValues;
48328
+ }
48329
+ /**
48330
+ * Reads one repeatable string option from raw CLI arguments.
48331
+ */
48332
+ function readRepeatableStringOption(args, flag) {
48333
+ const values = [];
48334
+ for (let index = 0; index < args.length; index += 1) {
48335
+ const argument = args[index];
48336
+ if (!argument) {
48337
+ continue;
48338
+ }
48339
+ if (argument === flag) {
48340
+ const nextValue = args[index + 1];
48341
+ if (nextValue !== undefined && !nextValue.startsWith('--')) {
48342
+ values.push(nextValue);
48343
+ index += 1;
48344
+ }
48345
+ continue;
48346
+ }
48347
+ if (argument.startsWith(`${flag}=`)) {
48348
+ values.push(argument.slice(flag.length + 1));
48349
+ }
48350
+ }
48351
+ return values;
48352
+ }
48353
+ /**
48354
+ * Resolves whether a prompt file should be ignored for this verification run.
48355
+ */
48356
+ function matchesIgnoredPromptFile(promptFile, ignoreValues) {
48357
+ const searchableValues = [promptFile.name, getPromptFileFirstLine(promptFile)].map((value) => value.toLowerCase());
48358
+ return ignoreValues.some((ignoreValue) => {
48359
+ const lowerCasedIgnoreValue = ignoreValue.toLowerCase();
48360
+ return searchableValues.some((searchableValue) => searchableValue.includes(lowerCasedIgnoreValue));
48361
+ });
48362
+ }
48363
+ /**
48364
+ * Returns the first meaningful line of the prompt file after the status line.
48365
+ */
48366
+ function getPromptFileFirstLine(promptFile) {
48367
+ var _a;
48368
+ const firstSection = promptFile.sections[0];
48369
+ const startLineIndex = (firstSection === null || firstSection === void 0 ? void 0 : firstSection.statusLineIndex) !== undefined ? firstSection.statusLineIndex + 1 : ((_a = firstSection === null || firstSection === void 0 ? void 0 : firstSection.startLine) !== null && _a !== void 0 ? _a : 0);
48370
+ for (let index = startLineIndex; index < promptFile.lines.length; index += 1) {
48371
+ const line = promptFile.lines[index];
48372
+ if (line !== undefined && line.trim() !== '') {
48373
+ return line.trim();
48374
+ }
48375
+ }
48376
+ return '';
48377
+ }
48219
48378
  /**
48220
48379
  * Displays the list of files that currently live in the prompts root.
48221
48380
  */
48222
48381
  function displayTopLevelFileList(promptFiles) {
48223
48382
  console.info(colors.cyan('\nTop-level prompt files:'));
48224
48383
  if (!promptFiles.length) {
48225
- console.info(colors.gray(' (no markdown files found in prompts/)'));
48384
+ console.info(colors.gray(' (no prompt markdown files found for this run in prompts/)'));
48226
48385
  return;
48227
48386
  }
48228
48387
  for (const file of promptFiles) {
@@ -48539,7 +48698,7 @@ function isNotFound(error) {
48539
48698
  }
48540
48699
  // Note: When run as a standalone script, call the exported function
48541
48700
  if (require.main === module) {
48542
- verifyPrompts(REVERSE_ORDER).catch((error) => {
48701
+ verifyPrompts(DEFAULT_VERIFY_PROMPTS_OPTIONS).catch((error) => {
48543
48702
  console.error(colors.bgRed('Prompt verification failed:'), error);
48544
48703
  process.exit(1);
48545
48704
  });
@@ -48547,7 +48706,8 @@ if (require.main === module) {
48547
48706
 
48548
48707
  var verifyPrompts$1 = /*#__PURE__*/Object.freeze({
48549
48708
  __proto__: null,
48550
- verifyPrompts: verifyPrompts
48709
+ verifyPrompts: verifyPrompts,
48710
+ partitionPromptFilesByIgnore: partitionPromptFilesByIgnore
48551
48711
  });
48552
48712
 
48553
48713
  /**