@promptbook/cli 0.114.0-25 → 0.114.0-26

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,6 +1,8 @@
1
1
  import { execFile } from 'child_process';
2
2
  import { createHash } from 'crypto';
3
3
  import { promisify } from 'util';
4
+ import { UnexpectedError } from '../../../../../src/errors/UnexpectedError';
5
+ import { spaceTrim } from '../../../../../src/utils/organization/spaceTrim';
4
6
  import { AGENT_PROJECT_RUNTIME_HOST } from './agentProjectRuntimeConstants';
5
7
  import type { AgentProjectRuntimeMode } from './AgentProjectRuntimeInfo';
6
8
  import { resolveAgentProjectRuntimeEnvironmentFlag } from './resolveAgentProjectRuntimeEnvironmentFlag';
@@ -44,8 +46,18 @@ const AGENT_PROJECT_PM2_PROCESS_LABEL_LENGTH = 36;
44
46
 
45
47
  /**
46
48
  * Max output retained from pm2 commands.
49
+ *
50
+ * Note: `pm2 jlist` embeds a full environment snapshot into every listed process, so the list grows by tens of
51
+ * kilobytes with each started project. A budget of one megabyte was exhausted by roughly a dozen projects,
52
+ * after which `execFile` killed every `pm2 jlist` call with `ERR_CHILD_PROCESS_STDIO_MAXBUFFER` and pm2
53
+ * appeared to know no process at all.
54
+ */
55
+ const PM2_COMMAND_MAX_BUFFER_BYTES = 64 * 1024 * 1024;
56
+
57
+ /**
58
+ * pm2 error reported when no process of the requested name is registered.
47
59
  */
48
- const PM2_COMMAND_MAX_BUFFER_BYTES = 1024 * 1024;
60
+ const PM2_MISSING_PROCESS_ERROR_PATTERN = /Process or Namespace .* not found/u;
49
61
 
50
62
  /**
51
63
  * Status snapshot for one pm2 process.
@@ -189,25 +201,46 @@ export async function startAgentProjectRuntimePm2Process(
189
201
  }
190
202
 
191
203
  /**
192
- * Stops one pm2 project process when it exists.
204
+ * Deletes every pm2 process registered under one name.
205
+ *
206
+ * Note: The delete is never skipped because pm2 could not be asked for its current process list. `pm2 start`
207
+ * appends another process instead of replacing the existing one when a process of the same name is already
208
+ * registered, so a delete which silently does nothing turns every later start of the same project into one
209
+ * more duplicate. Only pm2 itself reporting an unknown name counts as "already gone", every other failure
210
+ * is reported to the caller, which then must not start the project again.
193
211
  *
194
212
  * @param processName - pm2 process name.
195
213
  */
196
214
  export async function stopAgentProjectRuntimePm2Process(processName: string | null | undefined): Promise<void> {
197
- if (!processName) {
215
+ if (!processName || !isAgentProjectRuntimePm2Enabled()) {
198
216
  return;
199
217
  }
200
218
 
201
- const status = await resolveAgentProjectRuntimePm2Status(processName);
219
+ try {
220
+ await execFileAsync(PM2_COMMAND, ['delete', processName], {
221
+ maxBuffer: PM2_COMMAND_MAX_BUFFER_BYTES,
222
+ windowsHide: true,
223
+ });
224
+ } catch (error) {
225
+ if (isPm2MissingProcessError(error)) {
226
+ return;
227
+ }
202
228
 
203
- if (!status.isKnown) {
204
- return;
229
+ throw new UnexpectedError(
230
+ spaceTrim(
231
+ (block) => `
232
+ Failed to delete the pm2 process \`${processName}\` of an agent project.
233
+
234
+ Starting the project again would register one more pm2 process of the same name instead of
235
+ replacing the existing one, so the project was left alone.
236
+
237
+ **Cause:**
238
+ ${block(error instanceof Error ? error.message : String(error))}
239
+ `,
240
+ ),
241
+ );
205
242
  }
206
243
 
207
- await execFileAsync(PM2_COMMAND, ['delete', processName], {
208
- maxBuffer: PM2_COMMAND_MAX_BUFFER_BYTES,
209
- windowsHide: true,
210
- });
211
244
  await savePm2ProcessList();
212
245
  }
213
246
 
@@ -366,6 +399,19 @@ async function savePm2ProcessList(): Promise<void> {
366
399
  });
367
400
  }
368
401
 
402
+ /**
403
+ * Returns whether one failed pm2 command failed only because pm2 knows no process of the requested name.
404
+ */
405
+ function isPm2MissingProcessError(error: unknown): boolean {
406
+ if (!error || typeof error !== 'object') {
407
+ return false;
408
+ }
409
+
410
+ const { stderr } = error as { readonly stderr?: unknown };
411
+
412
+ return typeof stderr === 'string' && PM2_MISSING_PROCESS_ERROR_PATTERN.test(stderr);
413
+ }
414
+
369
415
  /**
370
416
  * Creates the fallback status for missing or unavailable pm2 processes.
371
417
  */
@@ -19,6 +19,7 @@ const IMMEDIATE_USER_CHAT_ANSWER_INSTRUCTION_COMMITMENTS = new Set<string>([
19
19
  'STYLE',
20
20
  'STYLES',
21
21
  'WRITING RULES',
22
+ 'WRITING RULE',
22
23
  'FORMAT',
23
24
  'FORMATS',
24
25
  'SCENARIO',
package/esm/index.es.js CHANGED
@@ -48,7 +48,7 @@ const BOOK_LANGUAGE_VERSION = '2.0.0';
48
48
  * @generated
49
49
  * @see https://github.com/webgptorg/promptbook
50
50
  */
51
- const PROMPTBOOK_ENGINE_VERSION = '0.114.0-25';
51
+ const PROMPTBOOK_ENGINE_VERSION = '0.114.0-26';
52
52
  /**
53
53
  * TODO: string_promptbook_version should be constrained to the all versions of Promptbook engine
54
54
  * Note: [💞] Ignore a discrepancy between file name and entity name
@@ -20063,29 +20063,36 @@ class UseUserLocationCommitmentDefinition extends BaseCommitmentDefinition {
20063
20063
  /**
20064
20064
  * `WRITING RULES` commitment definition.
20065
20065
  *
20066
- * It adds constraints that apply strictly to writing style and presentation
20067
- * rather than task-solving behavior.
20066
+ * The `WRITING RULES`/`WRITING RULE` commitment adds constraints that apply strictly to writing style
20067
+ * and presentation rather than task-solving behavior.
20068
+ *
20069
+ * Example usage in agent source:
20070
+ *
20071
+ * ```book
20072
+ * WRITING RULES Keep every response under 120 words
20073
+ * WRITING RULE Always end the message with an emoji
20074
+ * ```
20068
20075
  *
20069
20076
  * @private [🪔] Maybe export the commitments through some package
20070
20077
  */
20071
20078
  class WritingRulesCommitmentDefinition extends BaseCommitmentDefinition {
20072
- constructor() {
20073
- super('WRITING RULES');
20079
+ constructor(type = 'WRITING RULES') {
20080
+ super(type);
20074
20081
  }
20075
20082
  /**
20076
- * Short one-line description of `WRITING RULES`.
20083
+ * Short one-line description of `WRITING RULES`/`WRITING RULE`.
20077
20084
  */
20078
20085
  get description() {
20079
20086
  return 'Add writing-only constraints such as tone, length, formatting, or emoji usage.';
20080
20087
  }
20081
20088
  /**
20082
- * Icon for `WRITING RULES`.
20089
+ * Icon for `WRITING RULES`/`WRITING RULE`.
20083
20090
  */
20084
20091
  get icon() {
20085
20092
  return '📝';
20086
20093
  }
20087
20094
  /**
20088
- * Markdown documentation for `WRITING RULES`.
20095
+ * Markdown documentation for `WRITING RULES`/`WRITING RULE` commitment.
20089
20096
  */
20090
20097
  get documentation() {
20091
20098
  return spaceTrim$1(`
@@ -20095,6 +20102,7 @@ class WritingRulesCommitmentDefinition extends BaseCommitmentDefinition {
20095
20102
 
20096
20103
  ## Key aspects
20097
20104
 
20105
+ - All writing rules are treated equally regardless of singular/plural form.
20098
20106
  - Use it for writing-only constraints such as tone, formatting, length, emoji usage, punctuation, or reading level.
20099
20107
  - Do **not** use it for problem-solving behavior, policy, tool usage, or decision-making logic. Use \`RULE\` for that.
20100
20108
  - Newer writing-rules blocks override conflicting earlier writing-rules blocks.
@@ -20293,7 +20301,8 @@ const COMMITMENT_REGISTRY = [
20293
20301
  new LanguageCommitmentDefinition('LANGUAGE'),
20294
20302
  new LanguageCommitmentDefinition('LANGUAGES'),
20295
20303
  new WritingSampleCommitmentDefinition(),
20296
- new WritingRulesCommitmentDefinition(),
20304
+ new WritingRulesCommitmentDefinition('WRITING RULES'),
20305
+ new WritingRulesCommitmentDefinition('WRITING RULE'),
20297
20306
  new SampleCommitmentDefinition('SAMPLE'),
20298
20307
  new SampleCommitmentDefinition('EXAMPLE'),
20299
20308
  new FormatCommitmentDefinition('FORMAT'),