@promptbook/cli 0.112.0-136 → 0.112.0-138

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/README.md CHANGED
@@ -27,6 +27,26 @@ Create persistent AI agents that turn your company's scattered knowledge into ac
27
27
 
28
28
 
29
29
 
30
+ ### Standalone VPS
31
+
32
+ Run the standalone VPS installer only on a fresh server. Interactive mode asks for the installation values:
33
+
34
+ ```bash
35
+ sudo curl -fsSL https://raw.githubusercontent.com/webgptorg/promptbook/refs/heads/main/other/vps/install.sh | bash
36
+ ```
37
+
38
+ Non-interactive mode takes defaults from command-line options and skips initial code-runner CLI setup, which can be configured later from the UI or SSH:
39
+
40
+ ```bash
41
+ sudo curl -fsSL https://raw.githubusercontent.com/webgptorg/promptbook/refs/heads/main/other/vps/install.sh | bash -s -- \
42
+ --non-interactive \
43
+ --yes-i-understand-that-script-should-be-run-on-fresh-server \
44
+ --domain s24.ptbk.io \
45
+ --openai-api-key sk-proj-abcdef \
46
+ --sentry-dsn https://abc@def.ingest.de.sentry.io/123 \
47
+ --admin-password xxx
48
+ ```
49
+
30
50
 
31
51
 
32
52
  <blockquote style="color: #ff8811">
@@ -44,6 +44,12 @@ ptbk agents-server start --harness github-copilot --model gpt-5.4 --thinking-lev
44
44
  <a id="agents-server-env-session-secret"></a>
45
45
  - `SESSION_SECRET`: HMAC signing key used to sign the session cookie. Must be set explicitly in production — the server refuses to start session signing when missing instead of falling back to a hardcoded default. Use a long random string, for example the output of `openssl rand -hex 32`. Keep it separate from `ADMIN_PASSWORD` so a leak of either credential cannot forge the other.
46
46
 
47
+ <a id="agents-server-env-ptbk-agents-server-user-chat-worker-token"></a>
48
+ - `PTBK_AGENTS_SERVER_USER_CHAT_WORKER_TOKEN`: Shared internal token used to authorize background worker routes (`/api/internal/user-chat-jobs/run`, `/api/internal/user-chat-timeouts/run`, and `/api/internal/agent-runner-limits`). Must be set explicitly in production — the server refuses to resolve the worker token when missing instead of falling back to `ADMIN_PASSWORD`, `SUPABASE_SERVICE_ROLE_KEY`, or a hardcoded constant. Use a long random string, for example the output of `openssl rand -hex 32`. The CLI launcher (`ptbk agents-server start`) generates a per-process value automatically when the variable is empty so local development works without configuration.
49
+
50
+ <a id="agents-server-env-promptbook-team-agent-access-token"></a>
51
+ - `PROMPTBOOK_TEAM_AGENT_ACCESS_TOKEN`: Dedicated secret used by same-server `TEAM` calls to authorize access to private teammate agents. Must be a dedicated random string — falling back to `ADMIN_PASSWORD` or `SUPABASE_SERVICE_ROLE_KEY` would let a leak of one credential compromise both authentication boundaries. When not configured, same-server team access stays disabled (the integration fails closed) so leaving the variable empty is safe but disables the feature. Use a long random string, for example the output of `openssl rand -hex 32`.
52
+
47
53
  ## Creating servers
48
54
 
49
55
  When creating new Agents server, search across the repository for [☁]
@@ -32,7 +32,7 @@ GitHub App configuration now lives in **Metadata** so you can customize it per s
32
32
  - **GITHUB_APP_ID** – numeric GitHub App ID from the app settings.
33
33
  - **GITHUB_APP_SLUG** – slug used in `https://github.com/apps/<slug>`.
34
34
  - **GITHUB_APP_PRIVATE_KEY** – PEM-encoded private key. Replace literal newlines with `\n` when editing through the UI and keep the `BEGIN/END` markers.
35
- - **GITHUB_APP_STATE_SECRET** (optional, recommended) – random string used to sign OAuth/connect state. If unset, `ADMIN_PASSWORD` will be used as a fallback.
35
+ - **GITHUB_APP_STATE_SECRET** (optional, recommended) – random string used to sign OAuth/connect state.
36
36
 
37
37
  The values stored in Metadata override any legacy `.env` entries and can differ per server host.
38
38
 
@@ -27,6 +27,8 @@ const APP_URL = `http://127.0.0.1:${APP_PORT}`;
27
27
  const APP_E2E_ENV = {
28
28
  ADMIN_PASSWORD: 'e2e-admin-password',
29
29
  SESSION_SECRET: 'e2e-session-secret-must-differ-from-admin-password',
30
+ PTBK_AGENTS_SERVER_USER_CHAT_WORKER_TOKEN: 'e2e-user-chat-worker-token-must-differ-from-admin-password',
31
+ PROMPTBOOK_TEAM_AGENT_ACCESS_TOKEN: 'e2e-team-agent-access-token-must-differ-from-admin-password',
30
32
  NEXT_DIST_DIR: '.next-e2e',
31
33
  NEXT_PUBLIC_SITE_URL: APP_URL,
32
34
  PTBK_AGENTS_SERVER_DATABASE: 'supabase',
@@ -1,4 +1,5 @@
1
1
  import { NextResponse } from 'next/server';
2
+ import { isTimingSafeEqualString } from '../../../../../../../src/utils/isTimingSafeEqualString';
2
3
  import { resolveUserChatWorkerInternalToken } from '@/src/utils/userChat';
3
4
  import { getLocalAgentRunnerLimits } from '@/src/utils/serverLimits';
4
5
 
@@ -47,5 +48,5 @@ async function handleAgentRunnerLimitsRequest(request: Request) {
47
48
  */
48
49
  function isAuthorizedInternalWorkerRequest(request: Request): boolean {
49
50
  const token = request.headers.get('x-user-chat-worker-token');
50
- return token === resolveUserChatWorkerInternalToken();
51
+ return isTimingSafeEqualString(token, resolveUserChatWorkerInternalToken());
51
52
  }
@@ -5,6 +5,7 @@ import {
5
5
  triggerUserChatJobWorker,
6
6
  } from '@/src/utils/userChat';
7
7
  import { after, NextResponse } from 'next/server';
8
+ import { isTimingSafeEqualString } from '../../../../../../../../src/utils/isTimingSafeEqualString';
8
9
 
9
10
  /**
10
11
  * Allows one worker invocation to run for the platform maximum.
@@ -101,5 +102,5 @@ function isAuthorizedUserChatWorkerRequest(request: Request): boolean {
101
102
  */
102
103
  function isAuthorizedInternalWorkerRequest(request: Request): boolean {
103
104
  const token = request.headers.get('x-user-chat-worker-token');
104
- return token === resolveUserChatWorkerInternalToken();
105
+ return isTimingSafeEqualString(token, resolveUserChatWorkerInternalToken());
105
106
  }
@@ -223,7 +223,7 @@ async function synchronizeLocalUserChatJob(
223
223
  }
224
224
 
225
225
  const failedFileContent = await readOptionalTextFile(join(agentDirectoryPath, metadata.failedPath));
226
- if (failedFileContent !== null) {
226
+ if (failedFileContent !== null && job.status !== 'FAILED') {
227
227
  const failureReason = parseLocalChatFailedMessageBook({
228
228
  bookContent: failedFileContent,
229
229
  expectedMessagesBeforeAnswer: metadata.expectedMessagesBeforeAnswer,
@@ -3,6 +3,7 @@ import { cookies, headers } from 'next/headers';
3
3
  import { cache } from 'react';
4
4
  import { spaceTrim } from 'spacetrim';
5
5
  import { EnvironmentMismatchError } from '../../../../src/errors/EnvironmentMismatchError';
6
+ import { isTimingSafeEqualString } from '../../../../src/utils/isTimingSafeEqualString';
6
7
  import { isStandaloneVpsRawIpBootstrapActive } from './standaloneVpsRawIpBootstrap';
7
8
 
8
9
  /**
@@ -153,7 +154,7 @@ export function parseSessionToken(token: string | null | undefined): SessionUser
153
154
  const payload = Buffer.from(payloadBase64, 'base64').toString('utf-8');
154
155
  const expectedSignature = createHmac('sha256', getSessionSigningKey()).update(payload).digest('hex');
155
156
 
156
- if (signature !== expectedSignature) {
157
+ if (!isTimingSafeEqualString(signature, expectedSignature)) {
157
158
  return null;
158
159
  }
159
160
 
@@ -1,18 +1,72 @@
1
- import { createHash } from 'crypto';
1
+ import { randomBytes } from 'crypto';
2
+ import { spaceTrim } from 'spacetrim';
3
+ import { EnvironmentMismatchError } from '../../../../../src/errors/EnvironmentMismatchError';
4
+
5
+ /**
6
+ * Cached internal worker token resolved from `PTBK_AGENTS_SERVER_USER_CHAT_WORKER_TOKEN` on first use.
7
+ *
8
+ * Computed lazily so module imports never fail eagerly at build time and the
9
+ * environment can still be configured before the first internal worker request.
10
+ *
11
+ * @private internal cache of `resolveUserChatWorkerInternalToken`
12
+ */
13
+ let resolvedUserChatWorkerInternalToken: string | null = null;
2
14
 
3
15
  /**
4
16
  * Resolves the shared internal token used to protect background worker routes.
17
+ *
18
+ * In production, `PTBK_AGENTS_SERVER_USER_CHAT_WORKER_TOKEN` must be configured
19
+ * explicitly — falling back to any shared credential (in particular
20
+ * `ADMIN_PASSWORD` or `SUPABASE_SERVICE_ROLE_KEY`) or a hardcoded literal would
21
+ * let anyone who learns that value (or computes its publicly-known hash) drive
22
+ * internal worker jobs at will. Outside production, a per-process random token
23
+ * is generated on first use so local development works without configuration
24
+ * while still rejecting unsafe shared fallbacks.
25
+ *
26
+ * @returns Shared internal worker token.
27
+ * @throws {EnvironmentMismatchError} When the environment variable is missing in production.
28
+ *
29
+ * @private internal utility of Agents Server
5
30
  */
6
31
  export function resolveUserChatWorkerInternalToken(): string {
7
- if (process.env.PTBK_AGENTS_SERVER_USER_CHAT_WORKER_TOKEN) {
8
- return process.env.PTBK_AGENTS_SERVER_USER_CHAT_WORKER_TOKEN;
32
+ if (resolvedUserChatWorkerInternalToken !== null) {
33
+ return resolvedUserChatWorkerInternalToken;
34
+ }
35
+
36
+ const configuredToken = process.env.PTBK_AGENTS_SERVER_USER_CHAT_WORKER_TOKEN?.trim();
37
+ if (configuredToken) {
38
+ resolvedUserChatWorkerInternalToken = configuredToken;
39
+ return resolvedUserChatWorkerInternalToken;
9
40
  }
10
41
 
11
- const entropySource =
12
- process.env.SUPABASE_SERVICE_ROLE_KEY ||
13
- process.env.ADMIN_PASSWORD ||
14
- process.env.NEXT_PUBLIC_SITE_URL ||
15
- 'promptbook-user-chat-worker';
42
+ if (process.env.NODE_ENV === 'production') {
43
+ throw new EnvironmentMismatchError(
44
+ spaceTrim(`
45
+ Missing required \`PTBK_AGENTS_SERVER_USER_CHAT_WORKER_TOKEN\` environment variable in production.
46
+
47
+ The Agents Server protects its internal worker routes
48
+ (\`/api/internal/user-chat-jobs/run\`,
49
+ \`/api/internal/user-chat-timeouts/run\`, and
50
+ \`/api/internal/agent-runner-limits\`) with this shared token.
51
+ Reusing \`ADMIN_PASSWORD\`, \`SUPABASE_SERVICE_ROLE_KEY\`, or a
52
+ hardcoded fallback would let anyone who learns that value drive
53
+ background jobs at will.
54
+
55
+ **Fix:** set \`PTBK_AGENTS_SERVER_USER_CHAT_WORKER_TOKEN\` to a long
56
+ random string (for example the output of \`openssl rand -hex 32\`)
57
+ in the deployment environment and restart the server.
58
+ `),
59
+ );
60
+ }
16
61
 
17
- return createHash('sha256').update(`user-chat-worker:${entropySource}`).digest('hex');
62
+ resolvedUserChatWorkerInternalToken = randomBytes(32).toString('hex');
63
+ console.warn(
64
+ spaceTrim(`
65
+ \`PTBK_AGENTS_SERVER_USER_CHAT_WORKER_TOKEN\` is not configured — generated
66
+ a random per-process internal worker token for this non-production run.
67
+ Pending internal worker invocations will be invalidated on every restart
68
+ until \`PTBK_AGENTS_SERVER_USER_CHAT_WORKER_TOKEN\` is set.
69
+ `),
70
+ );
71
+ return resolvedUserChatWorkerInternalToken;
18
72
  }
@@ -23,6 +23,8 @@ export const VPS_ENVIRONMENT_VARIABLE_KEYS = [
23
23
  'NEXT_PUBLIC_SITE_URL',
24
24
  'ADMIN_PASSWORD',
25
25
  'SESSION_SECRET',
26
+ 'PTBK_AGENTS_SERVER_USER_CHAT_WORKER_TOKEN',
27
+ 'PROMPTBOOK_TEAM_AGENT_ACCESS_TOKEN',
26
28
  'OPENAI_API_KEY',
27
29
  'PTBK_HARNESS',
28
30
  'PTBK_MODEL',
package/esm/index.es.js CHANGED
@@ -59,7 +59,7 @@ const BOOK_LANGUAGE_VERSION = '2.0.0';
59
59
  * @generated
60
60
  * @see https://github.com/webgptorg/promptbook
61
61
  */
62
- const PROMPTBOOK_ENGINE_VERSION = '0.112.0-136';
62
+ const PROMPTBOOK_ENGINE_VERSION = '0.112.0-138';
63
63
  /**
64
64
  * TODO: string_promptbook_version should be constrained to the all versions of Promptbook engine
65
65
  * Note: [💞] Ignore a discrepancy between file name and entity name
@@ -2671,6 +2671,8 @@ const REQUIRED_AGENTS_SERVER_ENV_VARIABLES = [
2671
2671
  createAgentsServerEnvVariable('SUPABASE_SERVICE_ROLE_KEY', ''),
2672
2672
  createAgentsServerEnvVariable('SUPABASE_AUTO_MIGRATE', 'true'),
2673
2673
  createAgentsServerEnvVariable('ADMIN_PASSWORD', ''),
2674
+ createAgentsServerEnvVariable('PTBK_AGENTS_SERVER_USER_CHAT_WORKER_TOKEN', ''),
2675
+ createAgentsServerEnvVariable('PROMPTBOOK_TEAM_AGENT_ACCESS_TOKEN', ''),
2674
2676
  ];
2675
2677
  /**
2676
2678
  * Ensures `.env` contains all required Agents Server configuration entries.