@promptbook/cli 0.112.0-110 → 0.112.0-112

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 (44) hide show
  1. package/apps/agents-server/src/app/admin/servers/CreateServerDialog.tsx +16 -0
  2. package/apps/agents-server/src/app/admin/servers/useCreateServerWizard.ts +31 -5
  3. package/apps/agents-server/src/app/agents/[agentName]/_utils.ts +7 -5
  4. package/apps/agents-server/src/app/agents/[agentName]/api/user-chats/[chatId]/stream/route.ts +4 -86
  5. package/apps/agents-server/src/app/api/admin/servers/route.ts +5 -0
  6. package/apps/agents-server/src/app/api/metadata/route.ts +4 -0
  7. package/apps/agents-server/src/components/ApplicationErrorPage/ApplicationErrorPage.tsx +118 -12
  8. package/apps/agents-server/src/database/customJavascript.ts +62 -1
  9. package/apps/agents-server/src/database/customStylesheet.ts +60 -1
  10. package/apps/agents-server/src/database/getMetadata.ts +84 -3
  11. package/apps/agents-server/src/instrumentation.ts +3 -0
  12. package/apps/agents-server/src/utils/agentRouting/resolveAgentRouteTarget.ts +27 -62
  13. package/apps/agents-server/src/utils/errorReporting/applicationErrorHandling.ts +45 -0
  14. package/apps/agents-server/src/utils/errorReporting/refreshApplicationDocument.ts +10 -0
  15. package/apps/agents-server/src/utils/errorReporting/registerServerErrorSentryLogging.ts +331 -0
  16. package/apps/agents-server/src/utils/errorReporting/sendApplicationErrorReportToSentry.ts +8 -153
  17. package/apps/agents-server/src/utils/errorReporting/sentryStore.ts +177 -0
  18. package/apps/agents-server/src/utils/importAgent.ts +1 -57
  19. package/apps/agents-server/src/utils/importAgentWithFallback.ts +0 -10
  20. package/apps/agents-server/src/utils/serverManagement/createManagedServer/bootstrapManagedServer.ts +3 -1
  21. package/apps/agents-server/src/utils/serverManagement/createManagedServer/normalizeCreateServerInput.ts +6 -0
  22. package/apps/agents-server/src/utils/serverManagement/createManagedServer/seedServerDefaultAgents.ts +7 -3
  23. package/apps/agents-server/src/utils/serverManagement/createManagedServer.ts +5 -0
  24. package/apps/agents-server/src/utils/userChat/listUserChats.ts +109 -0
  25. package/apps/agents-server/src/utils/userChat.ts +0 -1
  26. package/esm/index.es.js +39 -13
  27. package/esm/index.es.js.map +1 -1
  28. package/esm/src/cli/cli-commands/agents-server/startAgentsServer.d.ts +2 -1
  29. package/esm/src/cli/cli-commands/agents-server/startAgentsServer.test.d.ts +1 -0
  30. package/esm/src/collection/agent-collection/constructors/agent-collection-in-supabase/AgentCollectionInSupabase.d.ts +22 -0
  31. package/esm/src/version.d.ts +1 -1
  32. package/package.json +1 -1
  33. package/src/cli/cli-commands/agents-server/startAgentsServer.ts +23 -2
  34. package/src/collection/agent-collection/constructors/agent-collection-in-supabase/AgentCollectionInSupabase.ts +91 -14
  35. package/src/other/templates/getTemplatesPipelineCollection.ts +801 -652
  36. package/src/version.ts +2 -2
  37. package/src/versions.txt +2 -0
  38. package/umd/index.umd.js +39 -13
  39. package/umd/index.umd.js.map +1 -1
  40. package/umd/src/cli/cli-commands/agents-server/startAgentsServer.d.ts +2 -1
  41. package/umd/src/cli/cli-commands/agents-server/startAgentsServer.test.d.ts +1 -0
  42. package/umd/src/collection/agent-collection/constructors/agent-collection-in-supabase/AgentCollectionInSupabase.d.ts +22 -0
  43. package/umd/src/version.d.ts +1 -1
  44. package/apps/agents-server/src/utils/userChat/getUserChatRevision.ts +0 -145
@@ -39,16 +39,6 @@ type ImportedAgentCacheRecord = {
39
39
  */
40
40
  readonly source: string_book;
41
41
 
42
- /**
43
- * Fetch implementation that produced this cached record.
44
- */
45
- readonly fetchImplementation: typeof fetch;
46
-
47
- /**
48
- * Timestamp when the source was last fetched successfully.
49
- */
50
- readonly cachedAt: number;
51
-
52
42
  /**
53
43
  * Last observed ETag returned by the remote `/api/book` endpoint.
54
44
  */
@@ -65,22 +55,6 @@ type ImportedAgentCacheRecord = {
65
55
  */
66
56
  const IMPORTED_AGENT_CACHE = new Map<string, ImportedAgentCacheRecord>();
67
57
 
68
- /**
69
- * Freshness window for successful imported agent books.
70
- *
71
- * This avoids a remote revalidation round trip on every server render while
72
- * still allowing federated/default parent updates to appear shortly after.
73
- */
74
- const IMPORTED_AGENT_CACHE_FRESH_TTL_MS = 60_000;
75
-
76
- /**
77
- * Upper bound for fetching one imported agent book.
78
- *
79
- * Imported agents are used while rendering agent profiles and lists, so one slow
80
- * federated/default parent must not block the entire Agents Server page render.
81
- */
82
- const IMPORTED_AGENT_BOOK_FETCH_TIMEOUT_MS = 3_000;
83
-
84
58
  /**
85
59
  * In-flight remote imports deduplicated by canonical agent identifier.
86
60
  */
@@ -127,26 +101,6 @@ function createImportCacheKey(
127
101
  return agentIdentification.replace(/\/+$/g, '');
128
102
  }
129
103
 
130
- /**
131
- * Returns `true` when a cached imported agent can be reused without revalidation.
132
- *
133
- * @param cachedImport - Cached successful import record.
134
- * @returns Whether the cached import is still fresh enough for immediate reuse.
135
- */
136
- function isImportedAgentCacheFresh(cachedImport: ImportedAgentCacheRecord): boolean {
137
- return Date.now() - cachedImport.cachedAt < IMPORTED_AGENT_CACHE_FRESH_TTL_MS;
138
- }
139
-
140
- /**
141
- * Returns `true` when the current fetch implementation can safely reuse the cached import.
142
- *
143
- * @param cachedImport - Cached successful import record.
144
- * @returns Whether the cache was produced by the active fetch implementation.
145
- */
146
- function isImportedAgentCacheCompatible(cachedImport: ImportedAgentCacheRecord): boolean {
147
- return cachedImport.fetchImplementation === globalThis.fetch;
148
- }
149
-
150
104
  /**
151
105
  * Extracts one text/book payload from a successful HTTP response.
152
106
  *
@@ -199,14 +153,7 @@ export async function importAgent(
199
153
  inheritancePath: options?.inheritancePath,
200
154
  } satisfies ImportAgentOptions;
201
155
  const cacheKey = createImportCacheKey(agentIdentification);
202
- const storedCachedImport = IMPORTED_AGENT_CACHE.get(cacheKey);
203
- const cachedImport =
204
- storedCachedImport && isImportedAgentCacheCompatible(storedCachedImport) ? storedCachedImport : undefined;
205
-
206
- if (cachedImport && isImportedAgentCacheFresh(cachedImport)) {
207
- return cachedImport.source;
208
- }
209
-
156
+ const cachedImport = IMPORTED_AGENT_CACHE.get(cacheKey);
210
157
  const existingRequest = PENDING_IMPORTED_AGENT_REQUESTS.get(cacheKey);
211
158
  if (existingRequest) {
212
159
  return existingRequest;
@@ -228,7 +175,6 @@ export async function importAgent(
228
175
  const response: Response = await fetch(agentBookUrl, {
229
176
  cache: 'no-store',
230
177
  headers,
231
- signal: AbortSignal.timeout(IMPORTED_AGENT_BOOK_FETCH_TIMEOUT_MS),
232
178
  });
233
179
 
234
180
  if (response.status === 304 && cachedImport) {
@@ -256,8 +202,6 @@ export async function importAgent(
256
202
  const source = await readImportedAgentSource(agentIdentification, response);
257
203
  IMPORTED_AGENT_CACHE.set(cacheKey, {
258
204
  source,
259
- fetchImplementation: globalThis.fetch,
260
- cachedAt: Date.now(),
261
205
  etag: response.headers.get('etag'),
262
206
  lastModified: response.headers.get('last-modified'),
263
207
  });
@@ -19,10 +19,6 @@ type FailedImportedAgentFallbackCacheRecord = {
19
19
  * Generated fallback book returned for the failed import.
20
20
  */
21
21
  readonly fallbackSource: string_book;
22
- /**
23
- * Fetch implementation that produced this failed-import fallback.
24
- */
25
- readonly fetchImplementation: typeof fetch;
26
22
  /**
27
23
  * Cache expiration timestamp in epoch milliseconds.
28
24
  */
@@ -126,11 +122,6 @@ function readCachedFailedImportedAgentFallback(cacheKey: string): string_book |
126
122
  return null;
127
123
  }
128
124
 
129
- if (cachedFallback.fetchImplementation !== globalThis.fetch) {
130
- cachedFailedImportedAgentFallbackByKey.delete(cacheKey);
131
- return null;
132
- }
133
-
134
125
  return cachedFallback.fallbackSource;
135
126
  }
136
127
 
@@ -143,7 +134,6 @@ function readCachedFailedImportedAgentFallback(cacheKey: string): string_book |
143
134
  function writeCachedFailedImportedAgentFallback(cacheKey: string, fallbackSource: string_book): void {
144
135
  cachedFailedImportedAgentFallbackByKey.set(cacheKey, {
145
136
  fallbackSource,
146
- fetchImplementation: globalThis.fetch,
147
137
  expiresAt: Date.now() + FAILED_IMPORTED_AGENT_FALLBACK_CACHE_TTL_MS,
148
138
  });
149
139
  }
@@ -47,7 +47,9 @@ export async function bootstrapManagedServer(input: NormalizedCreateServerInput)
47
47
  await applyManagedServerMigrations(client, input, sqlRecorder, migrationLogger);
48
48
  await seedServerUsers(client, input, sqlRecorder);
49
49
  await seedServerMetadata(client, input, sqlRecorder);
50
- await seedServerDefaultAgents(client, input, sqlRecorder);
50
+ if (input.isDefaultAgentsInstalled) {
51
+ await seedServerDefaultAgents(client, input, sqlRecorder);
52
+ }
51
53
 
52
54
  await client.query('COMMIT');
53
55
  sqlRecorder.addStatement('COMMIT');
@@ -51,6 +51,11 @@ export type NormalizedCreateServerInput = {
51
51
  * Initial metadata rows inserted during bootstrap.
52
52
  */
53
53
  readonly metadataEntries: ReadonlyArray<ServerMetadataSeedEntry>;
54
+
55
+ /**
56
+ * Whether bundled default agents from `agents/default` should be created during bootstrap.
57
+ */
58
+ readonly isDefaultAgentsInstalled: boolean;
54
59
  };
55
60
 
56
61
  /**
@@ -103,5 +108,6 @@ export function normalizeCreateServerInput(input: CreateServerInput): Normalized
103
108
  iconUrl,
104
109
  initialSettings: input.initialSettings,
105
110
  }),
111
+ isDefaultAgentsInstalled: input.isDefaultAgentsInstalled !== false,
106
112
  };
107
113
  }
@@ -1,5 +1,6 @@
1
1
  import { createAgentPersistenceRecords } from '../../../../../../src/collection/agent-collection/constructors/agent-collection-in-supabase/createAgentPersistenceRecords';
2
2
  import type { Client } from 'pg';
3
+ import { DEFAULT_AGENT_VISIBILITY } from '../../agentVisibility';
3
4
  import { loadDefaultAgentBooks } from '../../defaultAgents/loadDefaultAgentBooks';
4
5
  import { createInsertStatement, quoteIdentifier, type SqlRecorder } from './createSqlRecorder';
5
6
  import type { NormalizedCreateServerInput } from './normalizeCreateServerInput';
@@ -28,7 +29,7 @@ export async function seedServerDefaultAgents(
28
29
  const createdAt = new Date().toISOString();
29
30
  const { agentInsertRecord, agentHistoryInsertRecord } = createAgentPersistenceRecords(
30
31
  defaultAgentBook,
31
- { sortOrder: index },
32
+ { sortOrder: index, visibility: DEFAULT_AGENT_VISIBILITY },
32
33
  createdAt,
33
34
  );
34
35
 
@@ -45,9 +46,10 @@ export async function seedServerDefaultAgents(
45
46
  "promptbookEngineVersion",
46
47
  "usage",
47
48
  "folderId",
48
- "sortOrder"
49
+ "sortOrder",
50
+ "visibility"
49
51
  )
50
- VALUES ($1, $2, $3, $4, $5, $6, $7::jsonb, $8, $9::jsonb, $10, $11)
52
+ VALUES ($1, $2, $3, $4, $5, $6, $7::jsonb, $8, $9::jsonb, $10, $11, $12)
51
53
  `,
52
54
  [
53
55
  agentInsertRecord.agentName,
@@ -61,6 +63,7 @@ export async function seedServerDefaultAgents(
61
63
  JSON.stringify(agentInsertRecord.usage),
62
64
  agentInsertRecord.folderId ?? null,
63
65
  agentInsertRecord.sortOrder ?? index,
66
+ agentInsertRecord.visibility ?? DEFAULT_AGENT_VISIBILITY,
64
67
  ],
65
68
  );
66
69
  sqlRecorder.addStatement(
@@ -70,6 +73,7 @@ export async function seedServerDefaultAgents(
70
73
  usage: JSON.stringify(agentInsertRecord.usage),
71
74
  folderId: agentInsertRecord.folderId ?? null,
72
75
  sortOrder: agentInsertRecord.sortOrder ?? index,
76
+ visibility: agentInsertRecord.visibility ?? DEFAULT_AGENT_VISIBILITY,
73
77
  }),
74
78
  );
75
79
 
@@ -108,6 +108,11 @@ export type CreateServerInput = {
108
108
  */
109
109
  readonly additionalUsers?: ReadonlyArray<ServerSeedUserInput>;
110
110
 
111
+ /**
112
+ * Whether bundled default agents from `agents/default` should be created during bootstrap.
113
+ */
114
+ readonly isDefaultAgentsInstalled?: boolean;
115
+
111
116
  /**
112
117
  * Initial metadata values for the new server.
113
118
  */
@@ -1,5 +1,7 @@
1
1
  import { $getTableName } from '@/src/database/$getTableName';
2
2
  import { $provideClientSql } from '@/src/database/$provideClientSql';
3
+ import { isAgentsServerSqliteMode } from '@/src/database/agentsServerDatabaseMode';
4
+ import { $provideAgentsServerSqliteDatabase } from '@/src/database/sqlite/$provideAgentsServerSqliteDatabase';
3
5
  import type { ListUserChatsOptions, UserChatRecord } from './UserChatRecord';
4
6
  import type { UserChatSource } from './UserChatSource';
5
7
  import type { UserChatRow } from './UserChatRow';
@@ -30,6 +32,13 @@ const POSTGRES_UNDEFINED_COLUMN_ERROR_CODE = '42703';
30
32
  const CLIENT_SQL_MISSING_CONNECTION_MESSAGE_FRAGMENT =
31
33
  'Environment variable `POSTGRES_URL` or `DATABASE_URL` must be defined.';
32
34
 
35
+ /**
36
+ * SQLite expression that safely exposes chat messages as a JSON array.
37
+ *
38
+ * @private function of `userChat`
39
+ */
40
+ const SQLITE_CHAT_MESSAGES_JSON_EXPRESSION = `CASE WHEN json_valid(chat."messages") THEN chat."messages" ELSE '[]' END`;
41
+
33
42
  /**
34
43
  * Lists all user chats for one agent ordered by last activity.
35
44
  */
@@ -69,6 +78,10 @@ export async function listUserChats(options: ListUserChatsOptions): Promise<Arra
69
78
  * @private function of `userChat`
70
79
  */
71
80
  export async function listUserChatSummarySeeds(options: ListUserChatsOptions): Promise<Array<UserChatSummarySeed>> {
81
+ if (isAgentsServerSqliteMode()) {
82
+ return listUserChatSummarySeedsViaSqlite(options);
83
+ }
84
+
72
85
  if (!isDirectSqlConnectionConfigured()) {
73
86
  return listUserChatSummarySeedsViaSupabase(options);
74
87
  }
@@ -151,6 +164,92 @@ export async function listUserChatSummarySeeds(options: ListUserChatsOptions): P
151
164
  }
152
165
  }
153
166
 
167
+ /**
168
+ * Lists lightweight chat-summary seeds through direct SQLite JSON queries.
169
+ *
170
+ * @private function of `userChat`
171
+ */
172
+ async function listUserChatSummarySeedsViaSqlite(options: ListUserChatsOptions): Promise<Array<UserChatSummarySeed>> {
173
+ const userChatTableName = quoteIdentifier(await $getTableName('UserChat'));
174
+ const shouldLoadExternalChats = options.viewerIsAdmin && options.includeExternalChats;
175
+ const whereClause = shouldLoadExternalChats
176
+ ? `
177
+ chat."agentPermanentId" = ?
178
+ AND (chat."source" <> ? OR chat."userId" = ?)
179
+ `
180
+ : `
181
+ chat."userId" = ?
182
+ AND chat."agentPermanentId" = ?
183
+ AND chat."source" = ?
184
+ `;
185
+ const queryValues = shouldLoadExternalChats
186
+ ? [options.agentPermanentId, USER_CHAT_SOURCES.WEB_UI, options.userId]
187
+ : [options.userId, options.agentPermanentId, USER_CHAT_SOURCES.WEB_UI];
188
+
189
+ try {
190
+ const database = $provideAgentsServerSqliteDatabase();
191
+ const summaryRows = database
192
+ .prepare(
193
+ `
194
+ SELECT
195
+ chat."id",
196
+ chat."createdAt",
197
+ chat."updatedAt",
198
+ chat."lastMessageAt",
199
+ chat."title",
200
+ chat."source",
201
+ COALESCE(json_array_length(${SQLITE_CHAT_MESSAGES_JSON_EXPRESSION}), 0) AS "messagesCount",
202
+ COALESCE(
203
+ (
204
+ SELECT CAST(json_extract(message.value, '$.content') AS TEXT)
205
+ FROM json_each(${SQLITE_CHAT_MESSAGES_JSON_EXPRESSION}) AS message
206
+ WHERE UPPER(CAST(COALESCE(json_extract(message.value, '$.sender'), '') AS TEXT)) = 'USER'
207
+ ORDER BY CAST(message.key AS INTEGER) ASC
208
+ LIMIT 1
209
+ ),
210
+ ''
211
+ ) AS "firstUserMessageContent",
212
+ COALESCE(
213
+ (
214
+ SELECT CAST(json_extract(message.value, '$.content') AS TEXT)
215
+ FROM json_each(${SQLITE_CHAT_MESSAGES_JSON_EXPRESSION}) AS message
216
+ WHERE LENGTH(TRIM(CAST(COALESCE(json_extract(message.value, '$.content'), '') AS TEXT))) > 0
217
+ ORDER BY CAST(message.key AS INTEGER) DESC
218
+ LIMIT 1
219
+ ),
220
+ ''
221
+ ) AS "lastPreviewMessageContent",
222
+ COALESCE(
223
+ (
224
+ SELECT COUNT(*)
225
+ FROM json_each(${SQLITE_CHAT_MESSAGES_JSON_EXPRESSION}) AS message
226
+ WHERE
227
+ UPPER(CAST(COALESCE(json_extract(message.value, '$.sender'), '') AS TEXT)) IN ('AGENT', 'MODEL')
228
+ AND (
229
+ json_extract(message.value, '$.isComplete') = 0
230
+ OR LOWER(CAST(COALESCE(json_extract(message.value, '$.isComplete'), '') AS TEXT)) = 'false'
231
+ OR LOWER(CAST(COALESCE(json_extract(message.value, '$.lifecycleState'), '') AS TEXT)) IN ('queued', 'running')
232
+ )
233
+ ),
234
+ 0
235
+ ) AS "pendingAssistantMessageCount"
236
+ FROM ${userChatTableName} AS chat
237
+ WHERE ${whereClause}
238
+ ORDER BY chat."lastMessageAt" IS NULL ASC, chat."lastMessageAt" DESC, chat."updatedAt" DESC
239
+ `,
240
+ )
241
+ .all(...queryValues) as Array<UserChatSummarySeedSqlRow>;
242
+
243
+ return summaryRows.map(mapUserChatSummarySeedSqlRow);
244
+ } catch (error) {
245
+ if (!isUserChatSummarySeedSqliteFallbackError(error)) {
246
+ throw error;
247
+ }
248
+
249
+ return listUserChatSummarySeedsViaSupabase(options);
250
+ }
251
+ }
252
+
154
253
  /**
155
254
  * Raw SQL row shape loaded for lightweight chat summary seeds.
156
255
  *
@@ -260,6 +359,16 @@ function isUserChatSummarySeedSqlFallbackError(error: unknown): boolean {
260
359
  return /relation .* does not exist|column .* does not exist/i.test(errorMessage);
261
360
  }
262
361
 
362
+ /**
363
+ * Returns true when SQLite summary optimization should gracefully fallback to Supabase-shaped reads.
364
+ *
365
+ * @private function of `userChat`
366
+ */
367
+ function isUserChatSummarySeedSqliteFallbackError(error: unknown): boolean {
368
+ const errorMessage = error instanceof Error ? error.message : String(error);
369
+ return /no such table|no such column|no such function: json_|malformed JSON/i.test(errorMessage);
370
+ }
371
+
263
372
  /**
264
373
  * Loads chat-summary seeds using standard Supabase reads when SQL optimization is unavailable.
265
374
  *
@@ -13,7 +13,6 @@ export { getUserChat } from './userChat/getUserChat';
13
13
  export { getUserChatJob } from './userChat/getUserChatJob';
14
14
  export { getUserChatJobById } from './userChat/getUserChatJobById';
15
15
  export { getUserChatJobByClientMessageId } from './userChat/getUserChatJobByClientMessageId';
16
- export { getUserChatRevision } from './userChat/getUserChatRevision';
17
16
  export { heartbeatUserChatJob } from './userChat/heartbeatUserChatJob';
18
17
  export { listExpiredRunningUserChatJobs } from './userChat/listExpiredRunningUserChatJobs';
19
18
  export { listUserChats, listUserChatSummarySeeds } from './userChat/listUserChats';
package/esm/index.es.js CHANGED
@@ -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-110';
61
+ const PROMPTBOOK_ENGINE_VERSION = '0.112.0-112';
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
@@ -34200,17 +34200,23 @@ function buildCodexScript(options) {
34200
34200
  var _a;
34201
34201
  const delimiter = resolveShellHereDocumentDelimiter(CODEX_PROMPT_DELIMITER, options.prompt);
34202
34202
  const projectPath = toPosixPath(options.projectPath);
34203
- const loginMethodConfig = options.allowCredits
34204
- ? 'CODEX_LOGIN_METHOD_ARGUMENTS=()'
34205
- : spaceTrim(`
34206
- CODEX_LOGIN_METHOD_ARGUMENTS=(-c forced_login_method=chatgpt)
34207
- if [ "\${PTBK_OPENAI_CODEX_USE_API_KEY:-0}" = "1" ] && [ -n "\${OPENAI_API_KEY:-}" ]; then
34208
- CODEX_LOGIN_METHOD_ARGUMENTS=()
34209
- fi
34210
- `);
34203
+ const loginMethodConfig = spaceTrim(`
34204
+ ${options.allowCredits ? 'CODEX_LOGIN_METHOD_ARGUMENTS=()' : 'CODEX_LOGIN_METHOD_ARGUMENTS=(-c forced_login_method=chatgpt)'}
34205
+ CODEX_USE_API_KEY="\${PTBK_OPENAI_CODEX_USE_API_KEY:-}"
34206
+ if [ -z "\$CODEX_USE_API_KEY" ] && [ -n "\${OPENAI_API_KEY:-}" ]; then
34207
+ CODEX_USE_API_KEY=1
34208
+ fi
34209
+ if [ "\$CODEX_USE_API_KEY" = "1" ] && [ -n "\${OPENAI_API_KEY:-}" ]; then
34210
+ CODEX_LOGIN_METHOD_ARGUMENTS=(-c forced_login_method=api)
34211
+ fi
34212
+ `);
34211
34213
  const thinkingLevel = (_a = options.thinkingLevel) !== null && _a !== void 0 ? _a : DEFAULT_CODEX_THINKING_LEVEL;
34212
34214
  const lines = [
34213
- 'if [ -f .env ]; then',
34215
+ 'if [ -n "${PTBK_AGENTS_SERVER_ENV_FILE:-}" ] && [ -f "${PTBK_AGENTS_SERVER_ENV_FILE}" ]; then',
34216
+ 'set -a',
34217
+ 'source "${PTBK_AGENTS_SERVER_ENV_FILE}"',
34218
+ 'set +a',
34219
+ 'elif [ -f .env ]; then',
34214
34220
  'set -a',
34215
34221
  'source .env',
34216
34222
  'set +a',
@@ -34218,7 +34224,7 @@ function buildCodexScript(options) {
34218
34224
  '',
34219
34225
  loginMethodConfig,
34220
34226
  '',
34221
- 'if [ "${PTBK_OPENAI_CODEX_USE_API_KEY:-0}" != "1" ] || [ -z "${OPENAI_API_KEY:-}" ]; then',
34227
+ 'if [ "${CODEX_USE_API_KEY:-0}" != "1" ] || [ -z "${OPENAI_API_KEY:-}" ]; then',
34222
34228
  'unset OPENAI_API_KEY',
34223
34229
  'unset OPENAI_BASE_URL',
34224
34230
  'fi',
@@ -37272,6 +37278,12 @@ const PTBK_AGENTS_SERVER_SQLITE_PATH_ENV = 'PTBK_AGENTS_SERVER_SQLITE_PATH';
37272
37278
  * @private internal constant of `ptbk agents-server`
37273
37279
  */
37274
37280
  const PTBK_HOSTNAME_ENV = 'PTBK_HOSTNAME';
37281
+ /**
37282
+ * Explicit installed `.env` file passed by standalone VPS pm2/runtime launchers.
37283
+ *
37284
+ * @private internal constant of `ptbk agents-server`
37285
+ */
37286
+ const PTBK_AGENTS_SERVER_ENV_FILE_ENV = 'PTBK_AGENTS_SERVER_ENV_FILE';
37275
37287
  /**
37276
37288
  * Entropy size for the local-only token shared by the CLI pump and the Next app.
37277
37289
  *
@@ -37397,11 +37409,20 @@ async function prepareAgentsServerDevelopmentRuntime(appPath, runnerLogStream) {
37397
37409
  });
37398
37410
  }
37399
37411
  /**
37400
- * Loads launch-directory `.env` values without overriding explicit process environment.
37412
+ * Loads Agents Server runtime environment from the installed `.env` file when explicitly configured and falls back
37413
+ * to the launch-directory `.env`.
37401
37414
  *
37402
37415
  * @private internal utility of `ptbk agents-server`
37403
37416
  */
37404
37417
  function loadAgentsServerProjectEnvironment(launchWorkingDirectory) {
37418
+ var _a;
37419
+ const explicitEnvFilePath = (_a = process.env[PTBK_AGENTS_SERVER_ENV_FILE_ENV]) === null || _a === void 0 ? void 0 : _a.trim();
37420
+ if (explicitEnvFilePath) {
37421
+ const explicitLoadResult = dotenv.config({ path: explicitEnvFilePath, override: true });
37422
+ if (!explicitLoadResult.error) {
37423
+ return;
37424
+ }
37425
+ }
37405
37426
  dotenv.config({ path: join(launchWorkingDirectory, AGENTS_SERVER_PROJECT_ENV_FILE_NAME) });
37406
37427
  }
37407
37428
  /**
@@ -37427,7 +37448,12 @@ function startNextServer(options) {
37427
37448
  var _a;
37428
37449
  const nextRuntimeModeLabel = describeAgentsServerNextRuntimeMode(options.options.nextRuntimeMode);
37429
37450
  logRunnerEvent(options.logStreams.runner, `Starting the Agents Server Next process in ${nextRuntimeModeLabel} mode.`);
37430
- const nextArguments = [options.nextCliPath, options.options.nextRuntimeMode, '--port', String(options.options.port)];
37451
+ const nextArguments = [
37452
+ options.nextCliPath,
37453
+ options.options.nextRuntimeMode,
37454
+ '--port',
37455
+ String(options.options.port),
37456
+ ];
37431
37457
  const hostname = (_a = options.childEnvironment[PTBK_HOSTNAME_ENV]) === null || _a === void 0 ? void 0 : _a.trim();
37432
37458
  if (hostname) {
37433
37459
  nextArguments.push('--hostname', hostname);