bazaar-compute-node 0.1.24__tar.gz → 0.1.26__tar.gz
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.
- {bazaar_compute_node-0.1.24 → bazaar_compute_node-0.1.26}/PKG-INFO +1 -1
- {bazaar_compute_node-0.1.24 → bazaar_compute_node-0.1.26}/pyproject.toml +1 -1
- {bazaar_compute_node-0.1.24 → bazaar_compute_node-0.1.26}/pyproject.toml.orig +1 -1
- {bazaar_compute_node-0.1.24 → bazaar_compute_node-0.1.26}/src/bazaar_compute_node/app/agent.py +7 -10
- {bazaar_compute_node-0.1.24 → bazaar_compute_node-0.1.26}/src/bazaar_compute_node/app/command.py +287 -56
- {bazaar_compute_node-0.1.24 → bazaar_compute_node-0.1.26}/src/bazaar_compute_node/app/system_service.py +42 -8
- {bazaar_compute_node-0.1.24 → bazaar_compute_node-0.1.26}/src/bazaar_compute_node/bcc.py +34 -152
- {bazaar_compute_node-0.1.24 → bazaar_compute_node-0.1.26}/src/bazaar_compute_node/contrib/lark/channel.py +10 -8
- {bazaar_compute_node-0.1.24 → bazaar_compute_node-0.1.26}/src/bazaar_compute_node/contrib/sqlite/codec.py +152 -215
- bazaar_compute_node-0.1.26/src/bazaar_compute_node/contrib/sqlite/database.py +288 -0
- bazaar_compute_node-0.1.26/src/bazaar_compute_node/contrib/sqlite/executor.py +544 -0
- bazaar_compute_node-0.1.26/src/bazaar_compute_node/contrib/sqlite/migrations/__init__.py +57 -0
- bazaar_compute_node-0.1.26/src/bazaar_compute_node/contrib/sqlite/migrations/registry.py +164 -0
- bazaar_compute_node-0.1.24/src/bazaar_compute_node/contrib/sqlite/migrations.py → bazaar_compute_node-0.1.26/src/bazaar_compute_node/contrib/sqlite/migrations/v01_initial_node_schema.py +2 -339
- bazaar_compute_node-0.1.26/src/bazaar_compute_node/contrib/sqlite/migrations/v02_session_mapping_indexes.py +31 -0
- bazaar_compute_node-0.1.26/src/bazaar_compute_node/contrib/sqlite/migrations/v03_message_log_indexes.py +23 -0
- bazaar_compute_node-0.1.26/src/bazaar_compute_node/contrib/sqlite/migrations/v04_runtime_attempt_facts.py +46 -0
- bazaar_compute_node-0.1.26/src/bazaar_compute_node/contrib/sqlite/migrations/v05_inbound_message_references.py +33 -0
- bazaar_compute_node-0.1.26/src/bazaar_compute_node/contrib/sqlite/migrations/v06_inbound_message_reference_integrity.py +25 -0
- bazaar_compute_node-0.1.26/src/bazaar_compute_node/contrib/sqlite/migrations/v07_inbound_provider_identity.py +24 -0
- bazaar_compute_node-0.1.26/src/bazaar_compute_node/contrib/sqlite/migrations/v08_transient_stream_events.py +29 -0
- bazaar_compute_node-0.1.26/src/bazaar_compute_node/contrib/sqlite/migrations/v09_remove_runtime_events.py +20 -0
- bazaar_compute_node-0.1.26/src/bazaar_compute_node/contrib/sqlite/migrations/v10_add_outbound_attachments.py +17 -0
- bazaar_compute_node-0.1.26/src/bazaar_compute_node/contrib/sqlite/migrations/v11_remove_runtime_session_mapping.py +19 -0
- bazaar_compute_node-0.1.24/src/bazaar_compute_node/contrib/sqlite/reminder_migration.py → bazaar_compute_node-0.1.26/src/bazaar_compute_node/contrib/sqlite/migrations/v12_add_reminders.py +1 -1
- bazaar_compute_node-0.1.24/src/bazaar_compute_node/contrib/sqlite/agent_migration.py → bazaar_compute_node-0.1.26/src/bazaar_compute_node/contrib/sqlite/migrations/v13_add_agent_ownership.py +1 -1
- bazaar_compute_node-0.1.24/src/bazaar_compute_node/contrib/sqlite/inbox_migration.py → bazaar_compute_node-0.1.26/src/bazaar_compute_node/contrib/sqlite/migrations/v14_add_inbox_discovery_indexes.py +1 -1
- bazaar_compute_node-0.1.24/src/bazaar_compute_node/contrib/sqlite/handoff_migration.py → bazaar_compute_node-0.1.26/src/bazaar_compute_node/contrib/sqlite/migrations/v15_add_handoffs.py +1 -1
- bazaar_compute_node-0.1.26/src/bazaar_compute_node/contrib/sqlite/migrations/v16_remove_outbound_drafts.py +112 -0
- bazaar_compute_node-0.1.26/src/bazaar_compute_node/contrib/sqlite/migrations/v17_remove_agent_identity_triggers.py +20 -0
- bazaar_compute_node-0.1.26/src/bazaar_compute_node/contrib/sqlite/migrations/v18_unify_messages.py +400 -0
- {bazaar_compute_node-0.1.24 → bazaar_compute_node-0.1.26}/src/bazaar_compute_node/contrib/sqlite/plugin.py +2 -1
- bazaar_compute_node-0.1.26/src/bazaar_compute_node/contrib/sqlite/repository/__init__.py +5 -0
- bazaar_compute_node-0.1.26/src/bazaar_compute_node/contrib/sqlite/repository/base.py +168 -0
- bazaar_compute_node-0.1.26/src/bazaar_compute_node/contrib/sqlite/repository/facade.py +143 -0
- bazaar_compute_node-0.1.24/src/bazaar_compute_node/contrib/sqlite/handoff_repository.py → bazaar_compute_node-0.1.26/src/bazaar_compute_node/contrib/sqlite/repository/handoffs.py +8 -34
- bazaar_compute_node-0.1.26/src/bazaar_compute_node/contrib/sqlite/repository/messages.py +813 -0
- bazaar_compute_node-0.1.24/src/bazaar_compute_node/contrib/sqlite/reminder_repository.py → bazaar_compute_node-0.1.26/src/bazaar_compute_node/contrib/sqlite/repository/reminders.py +221 -233
- bazaar_compute_node-0.1.26/src/bazaar_compute_node/contrib/sqlite/repository/sessions.py +272 -0
- bazaar_compute_node-0.1.26/src/bazaar_compute_node/contrib/sqlite/storage.py +154 -0
- {bazaar_compute_node-0.1.24 → bazaar_compute_node-0.1.26}/src/bazaar_compute_node/contrib/telegram/channel.py +11 -8
- {bazaar_compute_node-0.1.24 → bazaar_compute_node-0.1.26}/src/bazaar_compute_node/contrib/wecom/channel.py +13 -10
- {bazaar_compute_node-0.1.24 → bazaar_compute_node-0.1.26}/src/bazaar_compute_node/core/channel.py +4 -4
- {bazaar_compute_node-0.1.24 → bazaar_compute_node-0.1.26}/src/bazaar_compute_node/core/command.py +51 -6
- {bazaar_compute_node-0.1.24 → bazaar_compute_node-0.1.26}/src/bazaar_compute_node/core/instruction.py +5 -1
- {bazaar_compute_node-0.1.24 → bazaar_compute_node-0.1.26}/src/bazaar_compute_node/core/models/__init__.py +4 -6
- {bazaar_compute_node-0.1.24 → bazaar_compute_node-0.1.26}/src/bazaar_compute_node/core/models/entities.py +116 -113
- {bazaar_compute_node-0.1.24 → bazaar_compute_node-0.1.26}/src/bazaar_compute_node/core/models/states.py +5 -20
- bazaar_compute_node-0.1.26/src/bazaar_compute_node/core/orchestration/command.py +475 -0
- {bazaar_compute_node-0.1.24 → bazaar_compute_node-0.1.26}/src/bazaar_compute_node/core/orchestration/error_feedback.py +4 -3
- {bazaar_compute_node-0.1.24 → bazaar_compute_node-0.1.26}/src/bazaar_compute_node/core/orchestration/handoff_command.py +49 -105
- {bazaar_compute_node-0.1.24 → bazaar_compute_node-0.1.26}/src/bazaar_compute_node/core/orchestration/reminder.py +9 -15
- {bazaar_compute_node-0.1.24 → bazaar_compute_node-0.1.26}/src/bazaar_compute_node/core/orchestration/reminder_command.py +36 -96
- {bazaar_compute_node-0.1.24 → bazaar_compute_node-0.1.26}/src/bazaar_compute_node/core/orchestration/services.py +3 -4
- {bazaar_compute_node-0.1.24 → bazaar_compute_node-0.1.26}/src/bazaar_compute_node/core/orchestration/session.py +100 -221
- {bazaar_compute_node-0.1.24 → bazaar_compute_node-0.1.26}/src/bazaar_compute_node/core/orchestration/turn.py +8 -8
- bazaar_compute_node-0.1.26/src/bazaar_compute_node/core/storage.py +753 -0
- bazaar_compute_node-0.1.24/src/bazaar_compute_node/contrib/sqlite/database.py +0 -220
- bazaar_compute_node-0.1.24/src/bazaar_compute_node/contrib/sqlite/repository.py +0 -767
- bazaar_compute_node-0.1.24/src/bazaar_compute_node/contrib/sqlite/scoped_repository.py +0 -903
- bazaar_compute_node-0.1.24/src/bazaar_compute_node/contrib/sqlite/storage.py +0 -74
- bazaar_compute_node-0.1.24/src/bazaar_compute_node/core/orchestration/command.py +0 -707
- bazaar_compute_node-0.1.24/src/bazaar_compute_node/core/storage.py +0 -220
- {bazaar_compute_node-0.1.24 → bazaar_compute_node-0.1.26}/LICENSE +0 -0
- {bazaar_compute_node-0.1.24 → bazaar_compute_node-0.1.26}/README.md +0 -0
- {bazaar_compute_node-0.1.24 → bazaar_compute_node-0.1.26}/src/bazaar_compute_node/__init__.py +0 -0
- {bazaar_compute_node-0.1.24 → bazaar_compute_node-0.1.26}/src/bazaar_compute_node/app/__init__.py +0 -0
- {bazaar_compute_node-0.1.24 → bazaar_compute_node-0.1.26}/src/bazaar_compute_node/app/agent_management.py +0 -0
- {bazaar_compute_node-0.1.24 → bazaar_compute_node-0.1.26}/src/bazaar_compute_node/app/application.py +0 -0
- {bazaar_compute_node-0.1.24 → bazaar_compute_node-0.1.26}/src/bazaar_compute_node/app/attachments.py +0 -0
- {bazaar_compute_node-0.1.24 → bazaar_compute_node-0.1.26}/src/bazaar_compute_node/app/config.py +0 -0
- {bazaar_compute_node-0.1.24 → bazaar_compute_node-0.1.26}/src/bazaar_compute_node/app/registry.py +0 -0
- {bazaar_compute_node-0.1.24 → bazaar_compute_node-0.1.26}/src/bazaar_compute_node/app/resource_dispatch.py +0 -0
- {bazaar_compute_node-0.1.24 → bazaar_compute_node-0.1.26}/src/bazaar_compute_node/app/transport.py +0 -0
- {bazaar_compute_node-0.1.24 → bazaar_compute_node-0.1.26}/src/bazaar_compute_node/app/windows_pipe.py +0 -0
- {bazaar_compute_node-0.1.24 → bazaar_compute_node-0.1.26}/src/bazaar_compute_node/app/wrapper.py +0 -0
- {bazaar_compute_node-0.1.24 → bazaar_compute_node-0.1.26}/src/bazaar_compute_node/cli.py +0 -0
- {bazaar_compute_node-0.1.24 → bazaar_compute_node-0.1.26}/src/bazaar_compute_node/contrib/__init__.py +0 -0
- {bazaar_compute_node-0.1.24 → bazaar_compute_node-0.1.26}/src/bazaar_compute_node/contrib/codex/__init__.py +0 -0
- {bazaar_compute_node-0.1.24 → bazaar_compute_node-0.1.26}/src/bazaar_compute_node/contrib/codex/approval.py +0 -0
- {bazaar_compute_node-0.1.24 → bazaar_compute_node-0.1.26}/src/bazaar_compute_node/contrib/codex/client.py +0 -0
- {bazaar_compute_node-0.1.24 → bazaar_compute_node-0.1.26}/src/bazaar_compute_node/contrib/codex/events.py +0 -0
- {bazaar_compute_node-0.1.24 → bazaar_compute_node-0.1.26}/src/bazaar_compute_node/contrib/codex/plugin.py +0 -0
- {bazaar_compute_node-0.1.24 → bazaar_compute_node-0.1.26}/src/bazaar_compute_node/contrib/codex/process.py +0 -0
- {bazaar_compute_node-0.1.24 → bazaar_compute_node-0.1.26}/src/bazaar_compute_node/contrib/codex/protocol.py +0 -0
- {bazaar_compute_node-0.1.24 → bazaar_compute_node-0.1.26}/src/bazaar_compute_node/contrib/codex/runtime.py +0 -0
- {bazaar_compute_node-0.1.24 → bazaar_compute_node-0.1.26}/src/bazaar_compute_node/contrib/lark/__init__.py +0 -0
- {bazaar_compute_node-0.1.24 → bazaar_compute_node-0.1.26}/src/bazaar_compute_node/contrib/lark/api.py +0 -0
- {bazaar_compute_node-0.1.24 → bazaar_compute_node-0.1.26}/src/bazaar_compute_node/contrib/lark/approval.py +0 -0
- {bazaar_compute_node-0.1.24 → bazaar_compute_node-0.1.26}/src/bazaar_compute_node/contrib/lark/attachments.py +0 -0
- {bazaar_compute_node-0.1.24 → bazaar_compute_node-0.1.26}/src/bazaar_compute_node/contrib/lark/frame.py +0 -0
- {bazaar_compute_node-0.1.24 → bazaar_compute_node-0.1.26}/src/bazaar_compute_node/contrib/lark/identity.py +0 -0
- {bazaar_compute_node-0.1.24 → bazaar_compute_node-0.1.26}/src/bazaar_compute_node/contrib/lark/outbound.py +0 -0
- {bazaar_compute_node-0.1.24 → bazaar_compute_node-0.1.26}/src/bazaar_compute_node/contrib/lark/plugin.py +0 -0
- {bazaar_compute_node-0.1.24 → bazaar_compute_node-0.1.26}/src/bazaar_compute_node/contrib/lark/transport.py +0 -0
- {bazaar_compute_node-0.1.24 → bazaar_compute_node-0.1.26}/src/bazaar_compute_node/contrib/logging/__init__.py +0 -0
- {bazaar_compute_node-0.1.24 → bazaar_compute_node-0.1.26}/src/bazaar_compute_node/contrib/logging/audit.py +0 -0
- {bazaar_compute_node-0.1.24 → bazaar_compute_node-0.1.26}/src/bazaar_compute_node/contrib/logging/plugin.py +0 -0
- {bazaar_compute_node-0.1.24 → bazaar_compute_node-0.1.26}/src/bazaar_compute_node/contrib/sqlite/__init__.py +0 -0
- {bazaar_compute_node-0.1.24 → bazaar_compute_node-0.1.26}/src/bazaar_compute_node/contrib/sqlite/handoff_codec.py +0 -0
- /bazaar_compute_node-0.1.24/src/bazaar_compute_node/contrib/sqlite/migration.py → /bazaar_compute_node-0.1.26/src/bazaar_compute_node/contrib/sqlite/migrations/model.py +0 -0
- {bazaar_compute_node-0.1.24 → bazaar_compute_node-0.1.26}/src/bazaar_compute_node/contrib/sqlite/reminder_codec.py +0 -0
- {bazaar_compute_node-0.1.24 → bazaar_compute_node-0.1.26}/src/bazaar_compute_node/contrib/telegram/__init__.py +0 -0
- {bazaar_compute_node-0.1.24 → bazaar_compute_node-0.1.26}/src/bazaar_compute_node/contrib/telegram/api.py +0 -0
- {bazaar_compute_node-0.1.24 → bazaar_compute_node-0.1.26}/src/bazaar_compute_node/contrib/telegram/approval.py +0 -0
- {bazaar_compute_node-0.1.24 → bazaar_compute_node-0.1.26}/src/bazaar_compute_node/contrib/telegram/attachments.py +0 -0
- {bazaar_compute_node-0.1.24 → bazaar_compute_node-0.1.26}/src/bazaar_compute_node/contrib/telegram/identity.py +0 -0
- {bazaar_compute_node-0.1.24 → bazaar_compute_node-0.1.26}/src/bazaar_compute_node/contrib/telegram/markdown.py +0 -0
- {bazaar_compute_node-0.1.24 → bazaar_compute_node-0.1.26}/src/bazaar_compute_node/contrib/telegram/outbound.py +0 -0
- {bazaar_compute_node-0.1.24 → bazaar_compute_node-0.1.26}/src/bazaar_compute_node/contrib/telegram/plugin.py +0 -0
- {bazaar_compute_node-0.1.24 → bazaar_compute_node-0.1.26}/src/bazaar_compute_node/contrib/wecom/__init__.py +0 -0
- {bazaar_compute_node-0.1.24 → bazaar_compute_node-0.1.26}/src/bazaar_compute_node/contrib/wecom/markdown.py +0 -0
- {bazaar_compute_node-0.1.24 → bazaar_compute_node-0.1.26}/src/bazaar_compute_node/contrib/wecom/outbound.py +0 -0
- {bazaar_compute_node-0.1.24 → bazaar_compute_node-0.1.26}/src/bazaar_compute_node/contrib/wecom/plugin.py +0 -0
- {bazaar_compute_node-0.1.24 → bazaar_compute_node-0.1.26}/src/bazaar_compute_node/core/__init__.py +0 -0
- {bazaar_compute_node-0.1.24 → bazaar_compute_node-0.1.26}/src/bazaar_compute_node/core/approval.py +0 -0
- {bazaar_compute_node-0.1.24 → bazaar_compute_node-0.1.26}/src/bazaar_compute_node/core/audit.py +0 -0
- {bazaar_compute_node-0.1.24 → bazaar_compute_node-0.1.26}/src/bazaar_compute_node/core/client.py +0 -0
- {bazaar_compute_node-0.1.24 → bazaar_compute_node-0.1.26}/src/bazaar_compute_node/core/concurrency.py +0 -0
- {bazaar_compute_node-0.1.24 → bazaar_compute_node-0.1.26}/src/bazaar_compute_node/core/correlation.py +0 -0
- {bazaar_compute_node-0.1.24 → bazaar_compute_node-0.1.26}/src/bazaar_compute_node/core/handoff.py +0 -0
- {bazaar_compute_node-0.1.24 → bazaar_compute_node-0.1.26}/src/bazaar_compute_node/core/inbox.py +0 -0
- {bazaar_compute_node-0.1.24 → bazaar_compute_node-0.1.26}/src/bazaar_compute_node/core/lifecycle.py +0 -0
- {bazaar_compute_node-0.1.24 → bazaar_compute_node-0.1.26}/src/bazaar_compute_node/core/models/handoff.py +0 -0
- {bazaar_compute_node-0.1.24 → bazaar_compute_node-0.1.26}/src/bazaar_compute_node/core/models/reminder.py +0 -0
- {bazaar_compute_node-0.1.24 → bazaar_compute_node-0.1.26}/src/bazaar_compute_node/core/models/reminder_owner.py +0 -0
- {bazaar_compute_node-0.1.24 → bazaar_compute_node-0.1.26}/src/bazaar_compute_node/core/observability.py +0 -0
- {bazaar_compute_node-0.1.24 → bazaar_compute_node-0.1.26}/src/bazaar_compute_node/core/orchestration/__init__.py +0 -0
- {bazaar_compute_node-0.1.24 → bazaar_compute_node-0.1.26}/src/bazaar_compute_node/core/orchestration/delivery.py +0 -0
- {bazaar_compute_node-0.1.24 → bazaar_compute_node-0.1.26}/src/bazaar_compute_node/core/outcomes.py +0 -0
- {bazaar_compute_node-0.1.24 → bazaar_compute_node-0.1.26}/src/bazaar_compute_node/core/paths.py +0 -0
- {bazaar_compute_node-0.1.24 → bazaar_compute_node-0.1.26}/src/bazaar_compute_node/core/reminder.py +0 -0
- {bazaar_compute_node-0.1.24 → bazaar_compute_node-0.1.26}/src/bazaar_compute_node/core/reminder_schedule.py +0 -0
- {bazaar_compute_node-0.1.24 → bazaar_compute_node-0.1.26}/src/bazaar_compute_node/core/runtime.py +0 -0
- {bazaar_compute_node-0.1.24 → bazaar_compute_node-0.1.26}/src/bazaar_compute_node/core/timerwheel.py +0 -0
- {bazaar_compute_node-0.1.24 → bazaar_compute_node-0.1.26}/src/bazaar_compute_node/i18n/__init__.py +0 -0
- {bazaar_compute_node-0.1.24 → bazaar_compute_node-0.1.26}/src/bazaar_compute_node/i18n/catalog.py +0 -0
- {bazaar_compute_node-0.1.24 → bazaar_compute_node-0.1.26}/src/bazaar_compute_node/i18n/english.py +0 -0
- {bazaar_compute_node-0.1.24 → bazaar_compute_node-0.1.26}/src/bazaar_compute_node/i18n/schinese.py +0 -0
{bazaar_compute_node-0.1.24 → bazaar_compute_node-0.1.26}/src/bazaar_compute_node/app/agent.py
RENAMED
|
@@ -279,8 +279,7 @@ class AgentApplication:
|
|
|
279
279
|
async def has_session(self, session_id: str) -> bool:
|
|
280
280
|
if not isinstance(session_id, str) or not session_id:
|
|
281
281
|
return False
|
|
282
|
-
|
|
283
|
-
return await transaction.get_bcn_session(session_id) is not None
|
|
282
|
+
return await self.storage.get_bcn_session(session_id) is not None
|
|
284
283
|
|
|
285
284
|
async def publish_reminder_wake(self, session_id: str) -> None:
|
|
286
285
|
if not self._started:
|
|
@@ -323,8 +322,7 @@ class AgentApplication:
|
|
|
323
322
|
self._wrapper_path = None
|
|
324
323
|
|
|
325
324
|
async def _referenced_attachment_paths(self) -> set[str]:
|
|
326
|
-
|
|
327
|
-
return set(await transaction.list_ready_attachment_paths())
|
|
325
|
+
return set(await self.storage.list_ready_attachment_paths())
|
|
328
326
|
|
|
329
327
|
def _adapter_context(self) -> Mapping[str, object]:
|
|
330
328
|
return {
|
|
@@ -370,12 +368,11 @@ class AgentApplication:
|
|
|
370
368
|
)
|
|
371
369
|
runtime_session_id = request.get("runtime_session_id")
|
|
372
370
|
session_capability = request.get("session_capability")
|
|
373
|
-
|
|
374
|
-
|
|
375
|
-
|
|
376
|
-
|
|
377
|
-
|
|
378
|
-
)
|
|
371
|
+
if await self.storage.get_bcn_session(session_id) is None:
|
|
372
|
+
raise CommandDispatchError(
|
|
373
|
+
"SESSION_NOT_FOUND",
|
|
374
|
+
f"unknown bcn session: {session_id}",
|
|
375
|
+
)
|
|
379
376
|
runtime_session = self.orchestrator.runtime_session(session_id)
|
|
380
377
|
if runtime_session is None or runtime_session_id != runtime_session.id:
|
|
381
378
|
raise CommandDispatchError(
|
{bazaar_compute_node-0.1.24 → bazaar_compute_node-0.1.26}/src/bazaar_compute_node/app/command.py
RENAMED
|
@@ -1,31 +1,78 @@
|
|
|
1
1
|
from __future__ import annotations
|
|
2
2
|
|
|
3
3
|
import asyncio
|
|
4
|
+
import json
|
|
4
5
|
from collections.abc import Awaitable, Callable, Mapping
|
|
5
6
|
from datetime import UTC, datetime
|
|
6
7
|
from time import time_ns
|
|
7
8
|
from typing import Annotated, Literal, cast
|
|
8
9
|
|
|
9
|
-
from pydantic import
|
|
10
|
+
from pydantic import (
|
|
11
|
+
BaseModel,
|
|
12
|
+
ConfigDict,
|
|
13
|
+
Field,
|
|
14
|
+
StrictBool,
|
|
15
|
+
StrictInt,
|
|
16
|
+
StrictStr,
|
|
17
|
+
ValidationError,
|
|
18
|
+
)
|
|
10
19
|
|
|
11
20
|
from ..core.command import (
|
|
12
21
|
ICommandService,
|
|
13
22
|
InboxListResult,
|
|
23
|
+
MessageSendFreshnessHold,
|
|
14
24
|
MessageSendHandoffRequired,
|
|
15
25
|
SessionNotFoundError,
|
|
16
26
|
)
|
|
17
27
|
from ..core.lifecycle import TimeoutBudget
|
|
18
|
-
from ..core.models import
|
|
28
|
+
from ..core.models import (
|
|
29
|
+
InboundAttachment,
|
|
30
|
+
InboxTargetSummary,
|
|
31
|
+
Message,
|
|
32
|
+
MessageDirection,
|
|
33
|
+
OutboundAttachment,
|
|
34
|
+
OutboundDeliveryState,
|
|
35
|
+
)
|
|
36
|
+
|
|
37
|
+
|
|
38
|
+
def _serialize_attachment(
|
|
39
|
+
attachment: InboundAttachment | OutboundAttachment,
|
|
40
|
+
) -> dict[str, object]:
|
|
41
|
+
if isinstance(attachment, InboundAttachment):
|
|
42
|
+
return {
|
|
43
|
+
"attachment_id": attachment.attachment_id,
|
|
44
|
+
"name": attachment.name,
|
|
45
|
+
"kind": attachment.kind,
|
|
46
|
+
"state": attachment.state,
|
|
47
|
+
"media_type": attachment.media_type,
|
|
48
|
+
"relative_path": attachment.relative_path,
|
|
49
|
+
"size_bytes": attachment.size_bytes,
|
|
50
|
+
"error": attachment.error,
|
|
51
|
+
"sha256": None,
|
|
52
|
+
}
|
|
53
|
+
return {
|
|
54
|
+
"attachment_id": None,
|
|
55
|
+
"name": attachment.name,
|
|
56
|
+
"kind": "file",
|
|
57
|
+
"state": "ready",
|
|
58
|
+
"media_type": attachment.media_type,
|
|
59
|
+
"relative_path": attachment.relative_path,
|
|
60
|
+
"size_bytes": attachment.size_bytes,
|
|
61
|
+
"error": None,
|
|
62
|
+
"sha256": attachment.sha256,
|
|
63
|
+
}
|
|
19
64
|
|
|
20
65
|
|
|
21
|
-
def
|
|
66
|
+
def serialize_message(message: Message) -> dict[str, object]:
|
|
22
67
|
return {
|
|
23
68
|
"seq": message.seq,
|
|
24
69
|
"message_id": message.message_id,
|
|
70
|
+
"direction": message.direction.value,
|
|
25
71
|
"session_id": message.session_id,
|
|
26
72
|
"channel_session_id": message.channel_session_id,
|
|
27
73
|
"channel": message.channel,
|
|
28
74
|
"received_at_ms": message.received_at_ms,
|
|
75
|
+
"created_at_ms": message.created_at_ms,
|
|
29
76
|
"provider_time_ms": message.provider_time_ms,
|
|
30
77
|
"sender": (
|
|
31
78
|
None
|
|
@@ -34,58 +81,21 @@ def serialize_inbound(message: InboundMessage) -> dict[str, object]:
|
|
|
34
81
|
),
|
|
35
82
|
"sender_kind": message.sender_kind.value,
|
|
36
83
|
"message_type": message.message_type,
|
|
37
|
-
"canonical_target": message.
|
|
84
|
+
"canonical_target": message.target,
|
|
38
85
|
"target_kind": message.target_kind.value,
|
|
39
86
|
"mentions_agent": message.mentions_agent,
|
|
40
87
|
"notifies_runtime": message.notifies_runtime,
|
|
41
88
|
"attachments": [
|
|
42
|
-
|
|
43
|
-
"attachment_id": attachment.attachment_id,
|
|
44
|
-
"name": attachment.name,
|
|
45
|
-
"kind": attachment.kind,
|
|
46
|
-
"state": attachment.state,
|
|
47
|
-
"media_type": attachment.media_type,
|
|
48
|
-
"relative_path": attachment.relative_path,
|
|
49
|
-
"size_bytes": attachment.size_bytes,
|
|
50
|
-
"error": attachment.error,
|
|
51
|
-
}
|
|
52
|
-
for attachment in message.attachments
|
|
89
|
+
_serialize_attachment(attachment) for attachment in message.attachments
|
|
53
90
|
],
|
|
54
91
|
"body": message.body,
|
|
55
92
|
"reply_to_message_id": message.reply_to_message_id,
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
"command_id": message.command_id,
|
|
63
|
-
"session_id": message.session_id,
|
|
64
|
-
"channel_session_id": message.channel_session_id,
|
|
65
|
-
"target": message.target,
|
|
66
|
-
"reply_to_message_id": message.reply_to_message_id,
|
|
67
|
-
"body": message.body,
|
|
68
|
-
"attachments": [
|
|
69
|
-
{
|
|
70
|
-
"name": attachment.name,
|
|
71
|
-
"relative_path": attachment.relative_path,
|
|
72
|
-
"media_type": attachment.media_type,
|
|
73
|
-
"size_bytes": attachment.size_bytes,
|
|
74
|
-
"sha256": attachment.sha256,
|
|
75
|
-
}
|
|
76
|
-
for attachment in message.attachments
|
|
77
|
-
],
|
|
78
|
-
"state": message.state.value,
|
|
79
|
-
"fresh_check_state": message.fresh_check_state.value,
|
|
80
|
-
"created_at_ms": message.created_at_ms,
|
|
81
|
-
"snapshot_seq": message.snapshot_seq,
|
|
82
|
-
"current_inbound_seq": message.current_inbound_seq,
|
|
83
|
-
"provider_attempted_at_ms": message.provider_attempted_at_ms,
|
|
84
|
-
"completed_at_ms": message.completed_at_ms,
|
|
85
|
-
"draft_saved_at_ms": message.draft_saved_at_ms,
|
|
86
|
-
"error_kind": message.error_kind,
|
|
87
|
-
"error_message": message.error_message,
|
|
88
|
-
"next_action": message.next_action,
|
|
93
|
+
"delivery_state": (
|
|
94
|
+
message.delivery_state.value
|
|
95
|
+
if message.direction is MessageDirection.OUTBOUND
|
|
96
|
+
and message.delivery_state is not None
|
|
97
|
+
else None
|
|
98
|
+
),
|
|
89
99
|
}
|
|
90
100
|
|
|
91
101
|
|
|
@@ -172,6 +182,7 @@ class _MessageSendRequest(_CommandRequest):
|
|
|
172
182
|
command_id: NonEmptyText
|
|
173
183
|
attachment_paths: list[NonEmptyText] = Field(default_factory=list)
|
|
174
184
|
reply_to_message_id: NonEmptyText | None = None
|
|
185
|
+
send_draft: StrictBool = False
|
|
175
186
|
created_at_ms: NonNegativeInt = Field(
|
|
176
187
|
default_factory=lambda: time_ns() // 1_000_000
|
|
177
188
|
)
|
|
@@ -426,10 +437,10 @@ class CommandDispatcher:
|
|
|
426
437
|
"ok": True,
|
|
427
438
|
"result": {
|
|
428
439
|
"messages": [
|
|
429
|
-
|
|
440
|
+
serialize_message(message) for message in result.messages
|
|
430
441
|
],
|
|
431
442
|
"referenced_messages": [
|
|
432
|
-
|
|
443
|
+
serialize_message(message)
|
|
433
444
|
for message in result.referenced_messages
|
|
434
445
|
],
|
|
435
446
|
"snapshot_seq": result.snapshot_seq,
|
|
@@ -458,10 +469,10 @@ class CommandDispatcher:
|
|
|
458
469
|
"ok": True,
|
|
459
470
|
"result": {
|
|
460
471
|
"messages": [
|
|
461
|
-
|
|
472
|
+
serialize_message(message) for message in result.messages
|
|
462
473
|
],
|
|
463
474
|
"referenced_messages": [
|
|
464
|
-
|
|
475
|
+
serialize_message(message)
|
|
465
476
|
for message in result.referenced_messages
|
|
466
477
|
],
|
|
467
478
|
"snapshot_seq": result.snapshot_seq,
|
|
@@ -480,17 +491,59 @@ class CommandDispatcher:
|
|
|
480
491
|
created_at_ms=request.created_at_ms,
|
|
481
492
|
attachment_paths=tuple(request.attachment_paths),
|
|
482
493
|
reply_to_message_id=request.reply_to_message_id,
|
|
494
|
+
send_draft=request.send_draft,
|
|
483
495
|
)
|
|
496
|
+
if isinstance(result, MessageSendFreshnessHold):
|
|
497
|
+
return {
|
|
498
|
+
"ok": True,
|
|
499
|
+
"result": {"text": format_freshness_hold(result)},
|
|
500
|
+
}
|
|
484
501
|
if isinstance(result, MessageSendHandoffRequired):
|
|
485
502
|
return {
|
|
486
503
|
"ok": True,
|
|
487
|
-
"result": {
|
|
488
|
-
"handoff_required": {"target": result.target},
|
|
489
|
-
},
|
|
504
|
+
"result": {"text": format_cross_session_hold(result.target)},
|
|
490
505
|
}
|
|
506
|
+
delivery_state = result.delivery_state
|
|
507
|
+
if delivery_state is None:
|
|
508
|
+
raise RuntimeError("outbound message has no delivery state")
|
|
509
|
+
if delivery_state is OutboundDeliveryState.SENT:
|
|
510
|
+
text = (
|
|
511
|
+
f"Message sent to {result.target}. Message ID: {result.message_id}"
|
|
512
|
+
)
|
|
513
|
+
elif delivery_state is OutboundDeliveryState.QUEUED:
|
|
514
|
+
text = (
|
|
515
|
+
f"Message queued to {result.target}. "
|
|
516
|
+
f"Message ID: {result.message_id}"
|
|
517
|
+
)
|
|
518
|
+
elif delivery_state is OutboundDeliveryState.PARTIAL:
|
|
519
|
+
raise CommandDispatchError(
|
|
520
|
+
"SEND_PARTIAL",
|
|
521
|
+
result.error_message
|
|
522
|
+
or "Message delivery was only partially confirmed.",
|
|
523
|
+
next_action=(
|
|
524
|
+
"Do not retry the complete message automatically; reconcile "
|
|
525
|
+
"confirmed delivery first."
|
|
526
|
+
),
|
|
527
|
+
)
|
|
528
|
+
elif delivery_state is OutboundDeliveryState.UNKNOWN:
|
|
529
|
+
raise CommandDispatchError(
|
|
530
|
+
"SEND_UNKNOWN",
|
|
531
|
+
result.error_message or "Message delivery outcome is unknown.",
|
|
532
|
+
next_action="Reconcile channel delivery before retrying.",
|
|
533
|
+
)
|
|
534
|
+
elif delivery_state is OutboundDeliveryState.FAILED:
|
|
535
|
+
raise CommandDispatchError(
|
|
536
|
+
"SEND_FAILED",
|
|
537
|
+
result.error_message or "Message delivery failed.",
|
|
538
|
+
next_action="Fix the provider error before retrying.",
|
|
539
|
+
)
|
|
540
|
+
else:
|
|
541
|
+
raise AssertionError(
|
|
542
|
+
f"message send returned unsupported state: {delivery_state.value}"
|
|
543
|
+
)
|
|
491
544
|
return {
|
|
492
545
|
"ok": True,
|
|
493
|
-
"result": {"
|
|
546
|
+
"result": {"text": text},
|
|
494
547
|
}
|
|
495
548
|
|
|
496
549
|
if resource == "thread" and command == "unfollow":
|
|
@@ -507,3 +560,181 @@ def format_message_time(timestamp_ms: int) -> str:
|
|
|
507
560
|
.astimezone()
|
|
508
561
|
.strftime("%Y-%m-%d %H:%M:%S")
|
|
509
562
|
)
|
|
563
|
+
|
|
564
|
+
|
|
565
|
+
def _message_timestamp(message: Mapping[str, object]) -> int:
|
|
566
|
+
for field_name in ("provider_time_ms", "received_at_ms", "created_at_ms"):
|
|
567
|
+
timestamp = message.get(field_name)
|
|
568
|
+
if timestamp is not None:
|
|
569
|
+
return cast(int, timestamp)
|
|
570
|
+
raise ValueError("message has no display timestamp")
|
|
571
|
+
|
|
572
|
+
|
|
573
|
+
def _message_header_fields(
|
|
574
|
+
message: Mapping[str, object],
|
|
575
|
+
) -> tuple[str, str, str, str, str | None, str]:
|
|
576
|
+
target = cast(str, message["canonical_target"])
|
|
577
|
+
message_id = cast(str, message["message_id"])
|
|
578
|
+
sender_kind = cast(str, message["sender_kind"])
|
|
579
|
+
sender_value = message["sender"]
|
|
580
|
+
sender: str | None = None
|
|
581
|
+
if sender_value is not None:
|
|
582
|
+
sender_mapping = cast(Mapping[str, object], sender_value)
|
|
583
|
+
sender_id = cast(str | None, sender_mapping.get("id"))
|
|
584
|
+
sender_name = cast(str | None, sender_mapping.get("name"))
|
|
585
|
+
if sender_id is not None and sender_name is not None:
|
|
586
|
+
sender = f"@{sender_id}({sender_name})"
|
|
587
|
+
elif sender_name is not None:
|
|
588
|
+
sender = f"@{sender_name}"
|
|
589
|
+
elif sender_id is not None:
|
|
590
|
+
sender = f"@{sender_id}"
|
|
591
|
+
return (
|
|
592
|
+
target,
|
|
593
|
+
message_id,
|
|
594
|
+
format_message_time(_message_timestamp(message)),
|
|
595
|
+
sender_kind,
|
|
596
|
+
sender,
|
|
597
|
+
cast(str, message["body"]),
|
|
598
|
+
)
|
|
599
|
+
|
|
600
|
+
|
|
601
|
+
def _attachment_suffix(message: Mapping[str, object]) -> str:
|
|
602
|
+
attachments = cast(list[Mapping[str, object]], message["attachments"])
|
|
603
|
+
if not attachments:
|
|
604
|
+
return ""
|
|
605
|
+
rendered: list[str] = []
|
|
606
|
+
for attachment in attachments:
|
|
607
|
+
name = cast(str, attachment["name"])
|
|
608
|
+
attachment_id = cast(str | None, attachment.get("attachment_id"))
|
|
609
|
+
state = cast(str, attachment["state"])
|
|
610
|
+
identity = f"id:{attachment_id}, " if attachment_id is not None else ""
|
|
611
|
+
if state == "ready":
|
|
612
|
+
path = cast(str, attachment["relative_path"])
|
|
613
|
+
rendered.append(f"{name} ({identity}path:{path})")
|
|
614
|
+
else:
|
|
615
|
+
error = cast(str, attachment["error"])
|
|
616
|
+
rendered.append(f"{name} ({identity}state:failed, error:{error})")
|
|
617
|
+
label = "attachment" if len(rendered) == 1 else "attachments"
|
|
618
|
+
return f" [{len(rendered)} {label}: {', '.join(rendered)}]"
|
|
619
|
+
|
|
620
|
+
|
|
621
|
+
def format_check_message(message: Mapping[str, object]) -> str:
|
|
622
|
+
target, message_id, timestamp, sender_kind, sender, body = _message_header_fields(
|
|
623
|
+
message
|
|
624
|
+
)
|
|
625
|
+
line = (
|
|
626
|
+
f"[target={target} msg={message_id} time={timestamp} "
|
|
627
|
+
f"type={sender_kind} mentioned={str(message['mentions_agent']).lower()}"
|
|
628
|
+
)
|
|
629
|
+
reply_to_message_id = message["reply_to_message_id"]
|
|
630
|
+
if reply_to_message_id is not None:
|
|
631
|
+
line += f" reply_to={cast(str, reply_to_message_id)}"
|
|
632
|
+
line += "] "
|
|
633
|
+
if sender is not None:
|
|
634
|
+
line += f"{sender} "
|
|
635
|
+
return line + body + _attachment_suffix(message)
|
|
636
|
+
|
|
637
|
+
|
|
638
|
+
def format_read_message(
|
|
639
|
+
message: Mapping[str, object],
|
|
640
|
+
*,
|
|
641
|
+
index: int,
|
|
642
|
+
count: int,
|
|
643
|
+
) -> str:
|
|
644
|
+
target, message_id, timestamp, sender_kind, sender, body = _message_header_fields(
|
|
645
|
+
message
|
|
646
|
+
)
|
|
647
|
+
fields = [
|
|
648
|
+
f"seq={cast(int, message['seq'])}",
|
|
649
|
+
f"msg={message_id}",
|
|
650
|
+
f"time={timestamp}",
|
|
651
|
+
f"type={sender_kind}",
|
|
652
|
+
f"replyTarget={target}",
|
|
653
|
+
f"mentioned={str(message['mentions_agent']).lower()}",
|
|
654
|
+
]
|
|
655
|
+
reply_to_message_id = message["reply_to_message_id"]
|
|
656
|
+
if reply_to_message_id is not None:
|
|
657
|
+
fields.append(f"replyTo={cast(str, reply_to_message_id)}")
|
|
658
|
+
line = f"[{index}/{count} {' '.join(fields)}] "
|
|
659
|
+
if sender is not None:
|
|
660
|
+
line += f"{sender} "
|
|
661
|
+
return line + body + _attachment_suffix(message)
|
|
662
|
+
|
|
663
|
+
|
|
664
|
+
def format_freshness_hold(result: MessageSendFreshnessHold) -> str:
|
|
665
|
+
messages = [serialize_message(message) for message in result.messages]
|
|
666
|
+
referenced_messages = [
|
|
667
|
+
serialize_message(message) for message in result.referenced_messages
|
|
668
|
+
]
|
|
669
|
+
shown = len(messages)
|
|
670
|
+
total = result.newer_message_total
|
|
671
|
+
message_label = "message" if total == 1 else "messages"
|
|
672
|
+
if messages:
|
|
673
|
+
bounds = f"{messages[0]['seq']}-{messages[-1]['seq']}"
|
|
674
|
+
else:
|
|
675
|
+
bounds = "none-none"
|
|
676
|
+
older_bound = (
|
|
677
|
+
"Older unreviewed messages are omitted."
|
|
678
|
+
if total > shown
|
|
679
|
+
else "No older unreviewed messages."
|
|
680
|
+
)
|
|
681
|
+
newer_bound = "No newer unreviewed messages."
|
|
682
|
+
lines = [
|
|
683
|
+
f"Unreviewed synced context for this target: {total} {message_label}.",
|
|
684
|
+
(
|
|
685
|
+
"Your message has been saved as a draft. Review this target's "
|
|
686
|
+
"synced context before sending."
|
|
687
|
+
),
|
|
688
|
+
"",
|
|
689
|
+
(
|
|
690
|
+
f"Read window: {shown} returned, seq {bounds}, oldest to newest. "
|
|
691
|
+
f"{older_bound} {newer_bound}"
|
|
692
|
+
),
|
|
693
|
+
"",
|
|
694
|
+
]
|
|
695
|
+
if referenced_messages:
|
|
696
|
+
lines.append(f"Referenced messages: {len(referenced_messages)}")
|
|
697
|
+
lines.extend(
|
|
698
|
+
format_read_message(message, index=index, count=len(referenced_messages))
|
|
699
|
+
for index, message in enumerate(referenced_messages, start=1)
|
|
700
|
+
)
|
|
701
|
+
lines.append("Window messages:")
|
|
702
|
+
lines.extend(
|
|
703
|
+
format_read_message(message, index=index, count=shown)
|
|
704
|
+
for index, message in enumerate(messages, start=1)
|
|
705
|
+
)
|
|
706
|
+
lines.extend(
|
|
707
|
+
(
|
|
708
|
+
"",
|
|
709
|
+
f"End of window: {shown}/{total} shown.",
|
|
710
|
+
"",
|
|
711
|
+
"To update the draft, send revised content normally:",
|
|
712
|
+
f" bcc message send --target {json.dumps(result.target)} <<'BCCMSG'",
|
|
713
|
+
" revised message",
|
|
714
|
+
" BCCMSG",
|
|
715
|
+
"To send the current draft unchanged:",
|
|
716
|
+
f" bcc message send --send-draft --target {json.dumps(result.target)}",
|
|
717
|
+
"You can also choose not to send anything.",
|
|
718
|
+
)
|
|
719
|
+
)
|
|
720
|
+
return "\n".join(lines)
|
|
721
|
+
|
|
722
|
+
|
|
723
|
+
def format_cross_session_hold(target: str) -> str:
|
|
724
|
+
quoted_target = json.dumps(target)
|
|
725
|
+
return "\n".join(
|
|
726
|
+
(
|
|
727
|
+
"Your message was not sent because the target belongs to another conversation.",
|
|
728
|
+
"",
|
|
729
|
+
(
|
|
730
|
+
"To continue this work in the target conversation, send a "
|
|
731
|
+
"self-contained handoff:"
|
|
732
|
+
),
|
|
733
|
+
f" bcc handoff send --target {quoted_target} <<'BCCMSG'",
|
|
734
|
+
" enough context to understand the background, goal, and next action",
|
|
735
|
+
" BCCMSG",
|
|
736
|
+
"This creates a handoff notice that wakes you in that conversation.",
|
|
737
|
+
"",
|
|
738
|
+
"You can also choose not to send anything.",
|
|
739
|
+
)
|
|
740
|
+
)
|
|
@@ -271,6 +271,29 @@ exit $exitCode
|
|
|
271
271
|
"""
|
|
272
272
|
|
|
273
273
|
|
|
274
|
+
def _vbs_literal(value: Path | str) -> str:
|
|
275
|
+
return '"' + str(value).replace('"', '""') + '"'
|
|
276
|
+
|
|
277
|
+
|
|
278
|
+
def _render_windows_launcher(wrapper_path: Path) -> str:
|
|
279
|
+
command = _vbs_literal(
|
|
280
|
+
"powershell.exe -NoProfile -NonInteractive -WindowStyle Hidden "
|
|
281
|
+
f'-ExecutionPolicy Bypass -File "{wrapper_path}"'
|
|
282
|
+
)
|
|
283
|
+
return f"""' {MANAGED_MARKER}
|
|
284
|
+
Option Explicit
|
|
285
|
+
|
|
286
|
+
Dim shell
|
|
287
|
+
Dim command
|
|
288
|
+
Dim exitCode
|
|
289
|
+
|
|
290
|
+
Set shell = CreateObject("WScript.Shell")
|
|
291
|
+
command = {command}
|
|
292
|
+
exitCode = shell.Run(command, 0, True)
|
|
293
|
+
WScript.Quit exitCode
|
|
294
|
+
"""
|
|
295
|
+
|
|
296
|
+
|
|
274
297
|
def _add_task_element(
|
|
275
298
|
parent: ElementTree.Element,
|
|
276
299
|
name: str,
|
|
@@ -284,7 +307,7 @@ def _add_task_element(
|
|
|
284
307
|
|
|
285
308
|
def _render_windows_task(
|
|
286
309
|
context: SystemServiceContext,
|
|
287
|
-
|
|
310
|
+
launcher_path: Path,
|
|
288
311
|
) -> bytes:
|
|
289
312
|
ElementTree.register_namespace("", TASK_NAMESPACE)
|
|
290
313
|
task = ElementTree.Element(
|
|
@@ -323,11 +346,11 @@ def _render_windows_task(
|
|
|
323
346
|
{"Context": "Author"},
|
|
324
347
|
)
|
|
325
348
|
action = _add_task_element(actions, "Exec")
|
|
326
|
-
_add_task_element(action, "Command", "
|
|
349
|
+
_add_task_element(action, "Command", "wscript.exe")
|
|
327
350
|
_add_task_element(
|
|
328
351
|
action,
|
|
329
352
|
"Arguments",
|
|
330
|
-
f'
|
|
353
|
+
f'//B //Nologo "{launcher_path}"',
|
|
331
354
|
)
|
|
332
355
|
_add_task_element(action, "WorkingDirectory", str(context.data_dir))
|
|
333
356
|
return ElementTree.tostring(task, encoding="utf-16", xml_declaration=True)
|
|
@@ -423,10 +446,11 @@ def _launchd_paths() -> tuple[Path, Path]:
|
|
|
423
446
|
)
|
|
424
447
|
|
|
425
448
|
|
|
426
|
-
def _windows_paths(context: SystemServiceContext) -> tuple[Path, Path]:
|
|
449
|
+
def _windows_paths(context: SystemServiceContext) -> tuple[Path, Path, Path]:
|
|
427
450
|
return (
|
|
428
451
|
context.data_dir / "bcn-system-service.xml",
|
|
429
452
|
context.data_dir / "bcn-system-service.ps1",
|
|
453
|
+
context.data_dir / "bcn-system-service.vbs",
|
|
430
454
|
)
|
|
431
455
|
|
|
432
456
|
|
|
@@ -462,16 +486,21 @@ def _install_macos(context: SystemServiceContext) -> None:
|
|
|
462
486
|
|
|
463
487
|
|
|
464
488
|
def _install_windows(context: SystemServiceContext) -> None:
|
|
465
|
-
xml_path, wrapper_path = _windows_paths(context)
|
|
489
|
+
xml_path, wrapper_path, launcher_path = _windows_paths(context)
|
|
466
490
|
context.data_dir.mkdir(parents=True, exist_ok=True)
|
|
467
491
|
_write_managed_file(
|
|
468
492
|
wrapper_path,
|
|
469
493
|
_render_windows_wrapper(context),
|
|
470
494
|
mode=0o600,
|
|
471
495
|
)
|
|
496
|
+
_write_managed_file(
|
|
497
|
+
launcher_path,
|
|
498
|
+
_render_windows_launcher(wrapper_path),
|
|
499
|
+
mode=0o600,
|
|
500
|
+
)
|
|
472
501
|
_write_managed_file(
|
|
473
502
|
xml_path,
|
|
474
|
-
_render_windows_task(context,
|
|
503
|
+
_render_windows_task(context, launcher_path),
|
|
475
504
|
mode=0o600,
|
|
476
505
|
)
|
|
477
506
|
_run_native_command(
|
|
@@ -686,15 +715,20 @@ def _uninstall_macos() -> None:
|
|
|
686
715
|
|
|
687
716
|
|
|
688
717
|
def _uninstall_windows(context: SystemServiceContext) -> None:
|
|
689
|
-
xml_path, wrapper_path = _windows_paths(context)
|
|
718
|
+
xml_path, wrapper_path, launcher_path = _windows_paths(context)
|
|
690
719
|
_stop_windows(context)
|
|
691
720
|
_run_native_command(
|
|
692
721
|
["schtasks", "/Delete", "/TN", WINDOWS_TASK_NAME, "/F"],
|
|
693
722
|
check=False,
|
|
694
723
|
)
|
|
695
724
|
_remove_managed_file(xml_path)
|
|
725
|
+
_remove_managed_file(launcher_path)
|
|
696
726
|
_remove_managed_file(wrapper_path)
|
|
697
|
-
if
|
|
727
|
+
if (
|
|
728
|
+
not xml_path.exists()
|
|
729
|
+
and not launcher_path.exists()
|
|
730
|
+
and not wrapper_path.exists()
|
|
731
|
+
):
|
|
698
732
|
print("system service uninstalled platform=windows", flush=True)
|
|
699
733
|
|
|
700
734
|
|