2020import sys
2121import time
2222from datetime import datetime
23+ from urllib .parse import urlencode , urlparse
2324
2425import requests
2526from dotenv import load_dotenv
@@ -51,13 +52,16 @@ def _first_present(*values):
5152class OdooAgentRuntime :
5253 """Runtime daemon that connects to Odoo and executes agent work."""
5354
54- def __init__ (self , odoo_url , api_key , name , poll_interval = 10 , max_concurrent = 3 ):
55+ def __init__ (self , odoo_url , api_key , name , poll_interval = 10 , max_concurrent = 3 , database = None ):
5556 self .odoo_url = odoo_url .rstrip ('/' )
5657 self .api_key = api_key
5758 self .name = name
5859 self .poll_interval = poll_interval
5960 self .max_concurrent = max_concurrent
61+ self .database = (database or '' ).strip () or None
6062 self .active_tasks = {}
63+ self ._database_bootstrapped = not self .database
64+ self ._database_bootstrap_failed = False
6165 self .session = requests .Session ()
6266 self .session .headers .update ({
6367 'X-API-Key' : api_key ,
@@ -67,8 +71,48 @@ def __init__(self, odoo_url, api_key, name, poll_interval=10, max_concurrent=3):
6771 def _api_url (self , path ):
6872 return f'{ self .odoo_url } { path } '
6973
74+ def _bootstrap_database_session (self ):
75+ """Select the configured Odoo database and retain its session cookie."""
76+ if self ._database_bootstrapped :
77+ return True
78+ if self ._database_bootstrap_failed :
79+ return False
80+
81+ login_url = f'{ self .odoo_url } /web/login?{ urlencode ({"db" : self .database })} '
82+ try :
83+ response = self .session .get (login_url , allow_redirects = True , timeout = 30 )
84+ response .raise_for_status ()
85+ except requests .exceptions .RequestException as exc :
86+ self ._database_bootstrap_failed = True
87+ logger .error (
88+ 'Database bootstrap failed for ODOO_DATABASE=%r: %s. '
89+ 'Runtime API calls were not attempted.' ,
90+ self .database ,
91+ exc ,
92+ )
93+ return False
94+
95+ final_url = getattr (response , 'url' , '' )
96+ if (
97+ isinstance (final_url , str )
98+ and urlparse (final_url ).path .rstrip ('/' ) == '/web/database/selector'
99+ ):
100+ self ._database_bootstrap_failed = True
101+ logger .error (
102+ 'Database bootstrap failed for ODOO_DATABASE=%r: redirected to '
103+ '/web/database/selector. Runtime API calls were not attempted.' ,
104+ self .database ,
105+ )
106+ return False
107+
108+ self ._database_bootstrapped = True
109+ logger .info ('Selected Odoo database %r for this runtime session.' , self .database )
110+ return True
111+
70112 def _request (self , method , path , ** kwargs ):
71113 """Make an API request with error handling."""
114+ if not self ._bootstrap_database_session ():
115+ return None
72116 try :
73117 resp = self .session .request (method , self ._api_url (path ), timeout = 30 , ** kwargs )
74118 resp .raise_for_status ()
@@ -310,7 +354,7 @@ def _resolve_command(self, agent_config, instruction, task_name, task_id):
310354
311355 cli_map = {
312356 'hermes' : ['hermes' , 'run' , '--task' , task_name , '--context' , instruction ],
313- 'opencode' : ['opencode' , 'run' , '--instruction' , instruction ],
357+ 'opencode' : ['opencode' , 'run' , instruction ],
314358 'openclaw' : ['openclaw' , 'agent' , '--task' , task_name , '--context' , instruction ],
315359 }
316360
@@ -390,6 +434,13 @@ def run(self):
390434 logger .info (f'Starting Odoo Agent Runtime: { self .name } ' )
391435 logger .info (f'Connecting to: { self .odoo_url } ' )
392436
437+ if not self ._bootstrap_database_session ():
438+ logger .error (
439+ 'Cannot start runtime because ODOO_DATABASE bootstrap failed. '
440+ 'Verify the database name and Odoo access; this is not an API key error.'
441+ )
442+ return False
443+
393444 if not self .send_heartbeat ():
394445 logger .error ('Failed to connect to Odoo. Check URL and API key.' )
395446 return False
@@ -443,6 +494,8 @@ def main():
443494 parser = argparse .ArgumentParser (description = 'Odoo Agent Runtime Daemon' )
444495 parser .add_argument ('--odoo-url' , default = os .getenv ('ODOO_URL' , 'http://localhost:8069' ),
445496 help = 'Odoo instance URL' )
497+ parser .add_argument ('--odoo-database' , default = os .getenv ('ODOO_DATABASE' , '' ),
498+ help = 'Optional Odoo database for multi-database instances' )
446499 parser .add_argument ('--api-key' , default = os .getenv ('API_KEY' , '' ),
447500 help = 'Runtime API key' )
448501 parser .add_argument ('--name' , default = os .getenv ('RUNTIME_NAME' , get_hostname ()),
@@ -464,6 +517,7 @@ def main():
464517 api_key = args .api_key ,
465518 name = args .name ,
466519 poll_interval = args .poll_interval ,
520+ database = args .odoo_database ,
467521 )
468522
469523 success = runtime .run ()
0 commit comments