donobu 5.38.1 → 5.39.1

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/dist/esm/main.js CHANGED
@@ -116,7 +116,7 @@ const DEFAULT_PORT = 31000;
116
116
  async function startDonobuServer({ port = DEFAULT_PORT, controlPanelHost = ControlPanel_1.NoOpControlPanelFactory, environ = envVars_1.env.pick('ANTHROPIC_API_KEY', 'ANTHROPIC_MODEL_NAME', 'AWS_ACCESS_KEY_ID', 'AWS_BEDROCK_MODEL_NAME', 'AWS_SECRET_ACCESS_KEY', 'BASE64_GPT_CONFIG', 'BROWSERBASE_API_KEY', 'BROWSERBASE_PROJECT_ID', 'DONOBU_DEPLOYMENT_ENVIRONMENT', 'DONOBU_API_BASE_URL', 'DONOBU_API_KEY', 'GOOGLE_GENERATIVE_AI_API_KEY', 'GOOGLE_GENERATIVE_AI_MODEL_NAME', 'OLLAMA_API_URL', 'OLLAMA_MODEL_NAME', 'OPENAI_API_KEY', 'OPENAI_API_MODEL_NAME', 'PERSISTENCE_PRIORITY'), } = {}) {
117
117
  try {
118
118
  const adminController = await AdminApiController_1.AdminApiController.create(environ.data.DONOBU_DEPLOYMENT_ENVIRONMENT ?? 'LOCAL', controlPanelHost, environ);
119
- adminController.start(port);
119
+ await adminController.start(port);
120
120
  Logger_1.appLogger.info(`Donobu API server available on http://localhost:${port}`);
121
121
  // Keep the process running until a process signal is detected.
122
122
  await new Promise((resolve) => {
@@ -132,8 +132,7 @@ async function startDonobuServer({ port = DEFAULT_PORT, controlPanelHost = Contr
132
132
  ['SIGTERM', 'SIGINT', 'SIGHUP'].forEach((signal) => {
133
133
  process.on(signal, () => {
134
134
  Logger_1.appLogger.info(`Received ${signal}, shutting down...`);
135
- adminController.stop();
136
- resolve();
135
+ void adminController.stop().finally(resolve);
137
136
  });
138
137
  });
139
138
  });
@@ -62,14 +62,20 @@ export declare class AdminApiController {
62
62
  static create(donobuDeploymentEnvironment: DonobuDeploymentEnvironment, controlPanelFactory: ControlPanelFactory, environ: EnvPick<typeof env, 'ANTHROPIC_API_KEY' | 'ANTHROPIC_MODEL_NAME' | 'AWS_ACCESS_KEY_ID' | 'AWS_BEDROCK_MODEL_NAME' | 'AWS_SECRET_ACCESS_KEY' | 'BASE64_GPT_CONFIG' | 'BROWSERBASE_API_KEY' | 'BROWSERBASE_PROJECT_ID' | 'DONOBU_API_BASE_URL' | 'DONOBU_API_KEY' | 'GOOGLE_GENERATIVE_AI_API_KEY' | 'GOOGLE_GENERATIVE_AI_MODEL_NAME' | 'OLLAMA_API_URL' | 'OLLAMA_MODEL_NAME' | 'OPENAI_API_KEY' | 'OPENAI_API_MODEL_NAME' | 'PERSISTENCE_PRIORITY'>): Promise<AdminApiController>;
63
63
  private constructor();
64
64
  /**
65
- * Serves the API and web assets; this blocks until stop() is called.
66
- * If the given port is 0, a random port is assigned.
65
+ * Binds the API/web-asset server to `port` and resolves once the socket is
66
+ * listening. If the given port is 0, a random port is assigned. Rejects on
67
+ * bind errors (e.g. EADDRINUSE) so callers — particularly those doing a
68
+ * stop/start bounce — can recover instead of crashing on an uncaught
69
+ * 'error' event.
67
70
  */
68
- start(port: number): void;
71
+ start(port: number): Promise<void>;
69
72
  /**
70
- * Stops the server.
73
+ * Stops accepting new connections and forcibly tears down any in-flight
74
+ * ones, then resolves. Forceful close keeps bounces predictable — a slow
75
+ * or stuck client request can't hold the port open. Callers that want to
76
+ * let in-flight work drain should do so before invoking stop().
71
77
  */
72
- stop(): void;
78
+ stop(): Promise<void>;
73
79
  private static setupExpressFramework;
74
80
  /**
75
81
  * Sets up URL error handler middleware to catch URL parsing/decoding errors.
@@ -87,22 +87,35 @@ class AdminApiController {
87
87
  this.app = app;
88
88
  }
89
89
  /**
90
- * Serves the API and web assets; this blocks until stop() is called.
91
- * If the given port is 0, a random port is assigned.
90
+ * Binds the API/web-asset server to `port` and resolves once the socket is
91
+ * listening. If the given port is 0, a random port is assigned. Rejects on
92
+ * bind errors (e.g. EADDRINUSE) so callers — particularly those doing a
93
+ * stop/start bounce — can recover instead of crashing on an uncaught
94
+ * 'error' event.
92
95
  */
93
- start(port) {
96
+ async start(port) {
94
97
  Logger_1.appLogger.debug(`Starting AdminController on port ${port}`);
95
- this.server = this.app.listen(port);
98
+ await new Promise((resolve, reject) => {
99
+ this.server = this.app.listen(port, () => resolve());
100
+ this.server.once('error', reject);
101
+ });
96
102
  }
97
103
  /**
98
- * Stops the server.
104
+ * Stops accepting new connections and forcibly tears down any in-flight
105
+ * ones, then resolves. Forceful close keeps bounces predictable — a slow
106
+ * or stuck client request can't hold the port open. Callers that want to
107
+ * let in-flight work drain should do so before invoking stop().
99
108
  */
100
- stop() {
109
+ async stop() {
101
110
  if (this.server) {
102
111
  const address = this.server.address();
103
112
  const port = typeof address === 'string' ? address : address?.port;
104
113
  Logger_1.appLogger.debug(`Stopping AdminController on port ${port}`);
105
- this.server.close();
114
+ const server = this.server;
115
+ await new Promise((resolve, reject) => {
116
+ server.close((err) => (err ? reject(err) : resolve()));
117
+ server.closeAllConnections();
118
+ });
106
119
  }
107
120
  }
108
121
  static async setupExpressFramework(donobuDeploymentEnvironment, controlPanelFactory, environ) {
package/dist/main.js CHANGED
@@ -116,7 +116,7 @@ const DEFAULT_PORT = 31000;
116
116
  async function startDonobuServer({ port = DEFAULT_PORT, controlPanelHost = ControlPanel_1.NoOpControlPanelFactory, environ = envVars_1.env.pick('ANTHROPIC_API_KEY', 'ANTHROPIC_MODEL_NAME', 'AWS_ACCESS_KEY_ID', 'AWS_BEDROCK_MODEL_NAME', 'AWS_SECRET_ACCESS_KEY', 'BASE64_GPT_CONFIG', 'BROWSERBASE_API_KEY', 'BROWSERBASE_PROJECT_ID', 'DONOBU_DEPLOYMENT_ENVIRONMENT', 'DONOBU_API_BASE_URL', 'DONOBU_API_KEY', 'GOOGLE_GENERATIVE_AI_API_KEY', 'GOOGLE_GENERATIVE_AI_MODEL_NAME', 'OLLAMA_API_URL', 'OLLAMA_MODEL_NAME', 'OPENAI_API_KEY', 'OPENAI_API_MODEL_NAME', 'PERSISTENCE_PRIORITY'), } = {}) {
117
117
  try {
118
118
  const adminController = await AdminApiController_1.AdminApiController.create(environ.data.DONOBU_DEPLOYMENT_ENVIRONMENT ?? 'LOCAL', controlPanelHost, environ);
119
- adminController.start(port);
119
+ await adminController.start(port);
120
120
  Logger_1.appLogger.info(`Donobu API server available on http://localhost:${port}`);
121
121
  // Keep the process running until a process signal is detected.
122
122
  await new Promise((resolve) => {
@@ -132,8 +132,7 @@ async function startDonobuServer({ port = DEFAULT_PORT, controlPanelHost = Contr
132
132
  ['SIGTERM', 'SIGINT', 'SIGHUP'].forEach((signal) => {
133
133
  process.on(signal, () => {
134
134
  Logger_1.appLogger.info(`Received ${signal}, shutting down...`);
135
- adminController.stop();
136
- resolve();
135
+ void adminController.stop().finally(resolve);
137
136
  });
138
137
  });
139
138
  });
@@ -62,14 +62,20 @@ export declare class AdminApiController {
62
62
  static create(donobuDeploymentEnvironment: DonobuDeploymentEnvironment, controlPanelFactory: ControlPanelFactory, environ: EnvPick<typeof env, 'ANTHROPIC_API_KEY' | 'ANTHROPIC_MODEL_NAME' | 'AWS_ACCESS_KEY_ID' | 'AWS_BEDROCK_MODEL_NAME' | 'AWS_SECRET_ACCESS_KEY' | 'BASE64_GPT_CONFIG' | 'BROWSERBASE_API_KEY' | 'BROWSERBASE_PROJECT_ID' | 'DONOBU_API_BASE_URL' | 'DONOBU_API_KEY' | 'GOOGLE_GENERATIVE_AI_API_KEY' | 'GOOGLE_GENERATIVE_AI_MODEL_NAME' | 'OLLAMA_API_URL' | 'OLLAMA_MODEL_NAME' | 'OPENAI_API_KEY' | 'OPENAI_API_MODEL_NAME' | 'PERSISTENCE_PRIORITY'>): Promise<AdminApiController>;
63
63
  private constructor();
64
64
  /**
65
- * Serves the API and web assets; this blocks until stop() is called.
66
- * If the given port is 0, a random port is assigned.
65
+ * Binds the API/web-asset server to `port` and resolves once the socket is
66
+ * listening. If the given port is 0, a random port is assigned. Rejects on
67
+ * bind errors (e.g. EADDRINUSE) so callers — particularly those doing a
68
+ * stop/start bounce — can recover instead of crashing on an uncaught
69
+ * 'error' event.
67
70
  */
68
- start(port: number): void;
71
+ start(port: number): Promise<void>;
69
72
  /**
70
- * Stops the server.
73
+ * Stops accepting new connections and forcibly tears down any in-flight
74
+ * ones, then resolves. Forceful close keeps bounces predictable — a slow
75
+ * or stuck client request can't hold the port open. Callers that want to
76
+ * let in-flight work drain should do so before invoking stop().
71
77
  */
72
- stop(): void;
78
+ stop(): Promise<void>;
73
79
  private static setupExpressFramework;
74
80
  /**
75
81
  * Sets up URL error handler middleware to catch URL parsing/decoding errors.
@@ -87,22 +87,35 @@ class AdminApiController {
87
87
  this.app = app;
88
88
  }
89
89
  /**
90
- * Serves the API and web assets; this blocks until stop() is called.
91
- * If the given port is 0, a random port is assigned.
90
+ * Binds the API/web-asset server to `port` and resolves once the socket is
91
+ * listening. If the given port is 0, a random port is assigned. Rejects on
92
+ * bind errors (e.g. EADDRINUSE) so callers — particularly those doing a
93
+ * stop/start bounce — can recover instead of crashing on an uncaught
94
+ * 'error' event.
92
95
  */
93
- start(port) {
96
+ async start(port) {
94
97
  Logger_1.appLogger.debug(`Starting AdminController on port ${port}`);
95
- this.server = this.app.listen(port);
98
+ await new Promise((resolve, reject) => {
99
+ this.server = this.app.listen(port, () => resolve());
100
+ this.server.once('error', reject);
101
+ });
96
102
  }
97
103
  /**
98
- * Stops the server.
104
+ * Stops accepting new connections and forcibly tears down any in-flight
105
+ * ones, then resolves. Forceful close keeps bounces predictable — a slow
106
+ * or stuck client request can't hold the port open. Callers that want to
107
+ * let in-flight work drain should do so before invoking stop().
99
108
  */
100
- stop() {
109
+ async stop() {
101
110
  if (this.server) {
102
111
  const address = this.server.address();
103
112
  const port = typeof address === 'string' ? address : address?.port;
104
113
  Logger_1.appLogger.debug(`Stopping AdminController on port ${port}`);
105
- this.server.close();
114
+ const server = this.server;
115
+ await new Promise((resolve, reject) => {
116
+ server.close((err) => (err ? reject(err) : resolve()));
117
+ server.closeAllConnections();
118
+ });
106
119
  }
107
120
  }
108
121
  static async setupExpressFramework(donobuDeploymentEnvironment, controlPanelFactory, environ) {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "donobu",
3
- "version": "5.38.1",
3
+ "version": "5.39.1",
4
4
  "description": "Create browser automations with an LLM agent and replay them as Playwright scripts.",
5
5
  "main": "dist/main.js",
6
6
  "module": "dist/esm/main.js",
@@ -79,7 +79,7 @@
79
79
  "ai": "^6.0.37",
80
80
  "better-sqlite3": "^12.6.2",
81
81
  "commander": "^13.0.0",
82
- "env-struct": "^1.3.2",
82
+ "env-struct": "^1.5.0",
83
83
  "express": "^5.2.1",
84
84
  "fast-json-stable-stringify": "^2.1.0",
85
85
  "prettier": "^3.8.0",