@ouro.bot/cli 0.1.0-alpha.39 → 0.1.0-alpha.390
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 +109 -14
- package/{AdoptionSpecialist.ouro → SerpentGuide.ouro}/agent.json +3 -2
- package/{AdoptionSpecialist.ouro → SerpentGuide.ouro}/psyche/SOUL.md +2 -2
- package/{AdoptionSpecialist.ouro → SerpentGuide.ouro}/psyche/identities/the-serpent.md +1 -1
- package/changelog.json +2368 -4
- package/dist/arc/attention-types.js +8 -0
- package/dist/arc/cares.js +140 -0
- package/dist/arc/episodes.js +117 -0
- package/dist/arc/intentions.js +133 -0
- package/dist/arc/json-store.js +117 -0
- package/dist/arc/obligations.js +237 -0
- package/dist/arc/packets.js +193 -0
- package/dist/arc/presence.js +185 -0
- package/dist/arc/task-lifecycle.js +65 -0
- package/dist/heart/active-work.js +832 -0
- package/dist/heart/agent-entry.js +58 -3
- package/dist/heart/attachments/image-normalize.js +194 -0
- package/dist/heart/attachments/materialize.js +97 -0
- package/dist/heart/attachments/originals.js +88 -0
- package/dist/heart/attachments/render.js +29 -0
- package/dist/heart/attachments/sources/adapter.js +2 -0
- package/dist/heart/attachments/sources/bluebubbles.js +156 -0
- package/dist/heart/attachments/sources/cli-local-file.js +78 -0
- package/dist/heart/attachments/sources/index.js +16 -0
- package/dist/heart/attachments/store.js +103 -0
- package/dist/heart/attachments/types.js +93 -0
- package/dist/heart/auth/auth-flow.js +378 -0
- package/dist/heart/bridges/manager.js +358 -0
- package/dist/heart/bridges/state-machine.js +135 -0
- package/dist/heart/bridges/store.js +123 -0
- package/dist/heart/bundle-state.js +168 -0
- package/dist/heart/commitments.js +111 -0
- package/dist/heart/config-registry.js +304 -0
- package/dist/heart/config.js +111 -128
- package/dist/heart/core.js +803 -259
- package/dist/heart/cross-chat-delivery.js +131 -0
- package/dist/heart/daemon/agent-config-check.js +384 -0
- package/dist/heart/daemon/agent-discovery.js +79 -3
- package/dist/heart/daemon/agent-service.js +360 -0
- package/dist/heart/daemon/agentic-repair.js +205 -0
- package/dist/heart/daemon/bluebubbles-health-diagnostics.js +122 -0
- package/dist/heart/daemon/cadence.js +70 -0
- package/dist/heart/daemon/cli-defaults.js +599 -0
- package/dist/heart/daemon/cli-exec.js +3384 -0
- package/dist/heart/daemon/cli-help.js +385 -0
- package/dist/heart/daemon/cli-parse.js +1097 -0
- package/dist/heart/daemon/cli-render-doctor.js +57 -0
- package/dist/heart/daemon/cli-render.js +560 -0
- package/dist/heart/daemon/cli-types.js +8 -0
- package/dist/heart/daemon/daemon-cli.js +28 -1582
- package/dist/heart/daemon/daemon-entry.js +356 -3
- package/dist/heart/daemon/daemon-health.js +141 -0
- package/dist/heart/daemon/daemon-runtime-sync.js +157 -12
- package/dist/heart/daemon/daemon-tombstone.js +236 -0
- package/dist/heart/daemon/daemon.js +684 -58
- package/dist/heart/daemon/doctor-types.js +8 -0
- package/dist/heart/daemon/doctor.js +419 -0
- package/dist/heart/daemon/health-monitor.js +79 -1
- package/dist/heart/daemon/hooks/agent-config-v2.js +33 -0
- package/dist/heart/daemon/hooks/bundle-meta.js +115 -1
- package/dist/heart/daemon/http-health-probe.js +80 -0
- package/dist/heart/daemon/inner-status.js +89 -0
- package/dist/heart/daemon/interactive-repair.js +182 -0
- package/dist/heart/daemon/launchd.js +46 -9
- package/dist/heart/daemon/log-tailer.js +82 -12
- package/dist/heart/daemon/logs-prune.js +105 -0
- package/dist/heart/daemon/message-router.js +17 -8
- package/dist/heart/daemon/os-cron-deps.js +134 -0
- package/dist/heart/daemon/ouro-bot-entry.js +4 -2
- package/dist/heart/daemon/ouro-entry.js +3 -1
- package/dist/heart/daemon/process-manager.js +201 -0
- package/dist/heart/daemon/provider-discovery.js +137 -0
- package/dist/heart/daemon/pulse.js +475 -0
- package/dist/heart/daemon/run-hooks.js +2 -0
- package/dist/heart/daemon/runtime-logging.js +67 -16
- package/dist/heart/daemon/runtime-metadata.js +73 -0
- package/dist/heart/daemon/runtime-mode.js +67 -0
- package/dist/heart/daemon/safe-mode.js +161 -0
- package/dist/heart/daemon/sense-manager.js +119 -30
- package/dist/heart/daemon/session-id-resolver.js +131 -0
- package/dist/heart/daemon/skill-management-installer.js +94 -0
- package/dist/heart/daemon/socket-client.js +307 -0
- package/dist/heart/daemon/stale-bundle-prune.js +96 -0
- package/dist/heart/daemon/startup-tui.js +237 -0
- package/dist/heart/daemon/task-scheduler.js +3 -25
- package/dist/heart/daemon/thoughts.js +510 -0
- package/dist/heart/daemon/up-progress.js +135 -0
- package/dist/heart/delegation.js +62 -0
- package/dist/heart/habits/habit-migration.js +181 -0
- package/dist/heart/habits/habit-parser.js +140 -0
- package/dist/heart/habits/habit-scheduler.js +371 -0
- package/dist/heart/{daemon → hatch}/hatch-flow.js +55 -126
- package/dist/heart/{daemon → hatch}/hatch-specialist.js +3 -3
- package/dist/heart/{daemon → hatch}/specialist-prompt.js +12 -9
- package/dist/heart/{daemon → hatch}/specialist-tools.js +33 -12
- package/dist/heart/identity.js +153 -65
- package/dist/heart/kept-notes.js +357 -0
- package/dist/heart/kicks.js +2 -20
- package/dist/heart/machine-identity.js +161 -0
- package/dist/heart/mcp/mcp-server.js +653 -0
- package/dist/heart/migrate-config.js +100 -0
- package/dist/heart/model-capabilities.js +59 -0
- package/dist/heart/outlook/outlook-http-hooks.js +64 -0
- package/dist/heart/outlook/outlook-http-response.js +7 -0
- package/dist/heart/outlook/outlook-http-routes.js +232 -0
- package/dist/heart/outlook/outlook-http-static.js +99 -0
- package/dist/heart/outlook/outlook-http-transport.js +116 -0
- package/dist/heart/outlook/outlook-http.js +99 -0
- package/dist/heart/outlook/outlook-read.js +28 -0
- package/dist/heart/outlook/outlook-types.js +27 -0
- package/dist/heart/outlook/outlook-view.js +195 -0
- package/dist/heart/outlook/readers/agent-machine.js +359 -0
- package/dist/heart/outlook/readers/continuity-readers.js +332 -0
- package/dist/heart/outlook/readers/runtime-readers.js +660 -0
- package/dist/heart/outlook/readers/sessions.js +232 -0
- package/dist/heart/outlook/readers/shared.js +111 -0
- package/dist/heart/platform.js +81 -0
- package/dist/heart/progress-story.js +42 -0
- package/dist/heart/provider-attempt.js +133 -0
- package/dist/heart/provider-binding-resolver.js +239 -0
- package/dist/heart/provider-credentials.js +379 -0
- package/dist/heart/provider-failover.js +266 -0
- package/dist/heart/provider-models.js +81 -0
- package/dist/heart/provider-ping.js +237 -0
- package/dist/heart/provider-state.js +216 -0
- package/dist/heart/provider-visibility.js +186 -0
- package/dist/heart/providers/anthropic-token.js +131 -0
- package/dist/heart/providers/anthropic.js +193 -55
- package/dist/heart/providers/azure.js +103 -12
- package/dist/heart/providers/error-classification.js +63 -0
- package/dist/heart/providers/github-copilot.js +145 -0
- package/dist/heart/providers/minimax-vlm.js +189 -0
- package/dist/heart/providers/minimax.js +29 -7
- package/dist/heart/providers/openai-codex.js +39 -29
- package/dist/heart/runtime-credentials.js +181 -0
- package/dist/heart/session-activity.js +190 -0
- package/dist/heart/session-events.js +855 -0
- package/dist/heart/session-transcript.js +167 -0
- package/dist/heart/start-of-turn-packet.js +345 -0
- package/dist/heart/streaming.js +36 -27
- package/dist/heart/sync.js +332 -0
- package/dist/heart/target-resolution.js +127 -0
- package/dist/heart/tempo.js +93 -0
- package/dist/heart/temporal-view.js +41 -0
- package/dist/heart/tool-activity-callbacks.js +36 -0
- package/dist/heart/tool-description.js +135 -0
- package/dist/heart/tool-friction.js +55 -0
- package/dist/heart/tool-loop.js +200 -0
- package/dist/heart/turn-context.js +351 -0
- package/dist/heart/turn-coordinator.js +28 -0
- package/dist/heart/{daemon → versioning}/ouro-bot-global-installer.js +1 -1
- package/dist/heart/{daemon → versioning}/ouro-bot-wrapper.js +1 -1
- package/dist/heart/versioning/ouro-path-installer.js +301 -0
- package/dist/heart/versioning/ouro-version-manager.js +295 -0
- package/dist/heart/{daemon → versioning}/staged-restart.js +40 -8
- package/dist/heart/{daemon → versioning}/update-checker.js +12 -2
- package/dist/heart/{daemon → versioning}/update-hooks.js +63 -59
- package/dist/mind/bundle-manifest.js +7 -1
- package/dist/mind/context.js +141 -94
- package/dist/mind/diary-integrity.js +60 -0
- package/dist/mind/{memory.js → diary.js} +84 -96
- package/dist/mind/embedding-provider.js +60 -0
- package/dist/mind/file-state.js +179 -0
- package/dist/mind/first-impressions.js +14 -1
- package/dist/mind/friends/channel.js +21 -0
- package/dist/mind/friends/group-context.js +144 -0
- package/dist/mind/friends/resolver.js +38 -1
- package/dist/mind/friends/store-file.js +39 -3
- package/dist/mind/friends/trust-explanation.js +74 -0
- package/dist/mind/friends/types.js +1 -1
- package/dist/mind/journal-index.js +161 -0
- package/dist/mind/note-search.js +268 -0
- package/dist/mind/obligation-steering.js +221 -0
- package/dist/mind/pending.js +66 -7
- package/dist/mind/prompt-refresh.js +3 -2
- package/dist/mind/prompt.js +946 -167
- package/dist/mind/provenance-trust.js +26 -0
- package/dist/mind/scrutiny.js +173 -0
- package/dist/mind/token-estimate.js +8 -12
- package/dist/nerves/cli-logging.js +7 -1
- package/dist/nerves/coverage/audit-rules.js +15 -6
- package/dist/nerves/coverage/audit.js +28 -2
- package/dist/nerves/coverage/cli.js +1 -1
- package/dist/nerves/coverage/file-completeness.js +83 -5
- package/dist/nerves/coverage/run-artifacts.js +1 -1
- package/dist/nerves/event-buffer.js +111 -0
- package/dist/nerves/index.js +224 -4
- package/dist/nerves/observation.js +20 -0
- package/dist/nerves/redact.js +79 -0
- package/dist/nerves/runtime.js +5 -1
- package/dist/outlook-ui/assets/index-BAcU08c-.css +1 -0
- package/dist/outlook-ui/assets/index-D7l3l4vY.js +61 -0
- package/dist/outlook-ui/index.html +15 -0
- package/dist/repertoire/ado-client.js +15 -56
- package/dist/repertoire/ado-semantic.js +11 -10
- package/dist/repertoire/api-client.js +97 -0
- package/dist/repertoire/bitwarden-store.js +365 -0
- package/dist/repertoire/bundle-templates.js +72 -0
- package/dist/repertoire/bw-installer.js +79 -0
- package/dist/repertoire/coding/codex-jsonl.js +64 -0
- package/dist/repertoire/coding/context-pack.js +330 -0
- package/dist/repertoire/coding/feedback.js +197 -30
- package/dist/repertoire/coding/manager.js +158 -9
- package/dist/repertoire/coding/spawner.js +55 -9
- package/dist/repertoire/coding/tools.js +170 -7
- package/dist/repertoire/commerce-errors.js +109 -0
- package/dist/repertoire/commerce-self-test.js +156 -0
- package/dist/repertoire/credential-access.js +107 -0
- package/dist/repertoire/duffel-client.js +185 -0
- package/dist/repertoire/github-client.js +14 -55
- package/dist/repertoire/graph-client.js +11 -52
- package/dist/repertoire/guardrails.js +371 -0
- package/dist/repertoire/mcp-client.js +255 -0
- package/dist/repertoire/mcp-manager.js +305 -0
- package/dist/repertoire/mcp-tools.js +63 -0
- package/dist/repertoire/shell-sessions.js +133 -0
- package/dist/repertoire/skills.js +15 -24
- package/dist/repertoire/stripe-client.js +131 -0
- package/dist/repertoire/tasks/board.js +43 -5
- package/dist/repertoire/tasks/fix.js +182 -0
- package/dist/repertoire/tasks/index.js +28 -10
- package/dist/repertoire/tasks/lifecycle.js +2 -2
- package/dist/repertoire/tasks/parser.js +3 -2
- package/dist/repertoire/tasks/scanner.js +194 -37
- package/dist/repertoire/tasks/transitions.js +16 -79
- package/dist/repertoire/tool-results.js +29 -0
- package/dist/repertoire/tools-attachments.js +317 -0
- package/dist/repertoire/tools-base.js +42 -690
- package/dist/repertoire/tools-bluebubbles.js +1 -0
- package/dist/repertoire/tools-bridge.js +141 -0
- package/dist/repertoire/tools-bundle.js +984 -0
- package/dist/repertoire/tools-config.js +185 -0
- package/dist/repertoire/tools-continuity.js +248 -0
- package/dist/repertoire/tools-credential.js +182 -0
- package/dist/repertoire/tools-files.js +342 -0
- package/dist/repertoire/tools-flight.js +224 -0
- package/dist/repertoire/tools-flow.js +105 -0
- package/dist/repertoire/tools-github.js +1 -7
- package/dist/repertoire/tools-notes.js +376 -0
- package/dist/repertoire/tools-session.js +739 -0
- package/dist/repertoire/tools-shell.js +120 -0
- package/dist/repertoire/tools-stripe.js +180 -0
- package/dist/repertoire/tools-surface.js +243 -0
- package/dist/repertoire/tools-teams.js +12 -62
- package/dist/repertoire/tools-travel.js +125 -0
- package/dist/repertoire/tools-user-profile.js +144 -0
- package/dist/repertoire/tools-vault.js +40 -0
- package/dist/repertoire/tools.js +144 -115
- package/dist/repertoire/travel-api-client.js +360 -0
- package/dist/repertoire/user-profile.js +118 -0
- package/dist/repertoire/vault-setup.js +246 -0
- package/dist/repertoire/vault-unlock.js +366 -0
- package/dist/scripts/claude-code-hook.js +41 -0
- package/dist/scripts/claude-code-stop-hook.js +47 -0
- package/dist/senses/attention-queue.js +116 -0
- package/dist/senses/bluebubbles/attachment-cache.js +53 -0
- package/dist/senses/bluebubbles/attachment-download.js +137 -0
- package/dist/senses/{bluebubbles-client.js → bluebubbles/client.js} +260 -9
- package/dist/senses/bluebubbles/entry.js +70 -0
- package/dist/senses/bluebubbles/inbound-log.js +113 -0
- package/dist/senses/bluebubbles/index.js +1620 -0
- package/dist/senses/{bluebubbles-media.js → bluebubbles/media.js} +121 -70
- package/dist/senses/{bluebubbles-model.js → bluebubbles/model.js} +33 -12
- package/dist/senses/{bluebubbles-mutation-log.js → bluebubbles/mutation-log.js} +46 -6
- package/dist/senses/bluebubbles/replay.js +129 -0
- package/dist/senses/bluebubbles/runtime-state.js +109 -0
- package/dist/senses/{bluebubbles-session-cleanup.js → bluebubbles/session-cleanup.js} +1 -1
- package/dist/senses/cli/bracketed-paste.js +82 -0
- package/dist/senses/cli/image-paste.js +287 -0
- package/dist/senses/cli/image-ref-navigation.js +75 -0
- package/dist/senses/cli/ink-app.js +156 -0
- package/dist/senses/cli/inline-diff.js +64 -0
- package/dist/senses/cli/input-keys.js +174 -0
- package/dist/senses/cli/kill-ring.js +86 -0
- package/dist/senses/cli/message-list.js +51 -0
- package/dist/senses/cli/ouro-tui.js +605 -0
- package/dist/senses/cli/spinner-imperative.js +135 -0
- package/dist/senses/cli/spinner.js +101 -0
- package/dist/senses/cli/status-line.js +60 -0
- package/dist/senses/cli/streaming-markdown.js +526 -0
- package/dist/senses/cli/tool-display.js +83 -0
- package/dist/senses/cli/tool-render.js +85 -0
- package/dist/senses/cli/tui-store.js +240 -0
- package/dist/senses/cli/virtual-list.js +35 -0
- package/dist/senses/cli-entry.js +60 -8
- package/dist/senses/cli-layout.js +187 -0
- package/dist/senses/cli.js +526 -211
- package/dist/senses/commands.js +66 -3
- package/dist/senses/continuity.js +94 -0
- package/dist/senses/habit-turn-message.js +108 -0
- package/dist/senses/inner-dialog-worker.js +112 -19
- package/dist/senses/inner-dialog.js +600 -95
- package/dist/senses/pipeline.js +539 -61
- package/dist/senses/proactive-content-guard.js +51 -0
- package/dist/senses/shared-turn.js +205 -0
- package/dist/senses/surface-tool.js +68 -0
- package/dist/senses/teams-entry.js +60 -8
- package/dist/senses/teams.js +569 -237
- package/dist/senses/trust-gate.js +6 -7
- package/package.json +28 -7
- package/skills/agent-commerce.md +106 -0
- package/skills/browser-navigation.md +110 -0
- package/skills/commerce-setup-guide.md +116 -0
- package/skills/commerce-setup.md +84 -0
- package/skills/configure-dev-tools.md +101 -0
- package/skills/travel-planning.md +134 -0
- package/dist/heart/daemon/ouro-path-installer.js +0 -178
- package/dist/heart/daemon/subagent-installer.js +0 -134
- package/dist/mind/associative-recall.js +0 -197
- package/dist/senses/bluebubbles-entry.js +0 -11
- package/dist/senses/bluebubbles.js +0 -832
- package/dist/senses/debug-activity.js +0 -127
- package/subagents/README.md +0 -60
- package/subagents/work-doer.md +0 -235
- package/subagents/work-merger.md +0 -618
- package/subagents/work-planner.md +0 -382
- /package/{AdoptionSpecialist.ouro → SerpentGuide.ouro}/psyche/identities/basilisk.md +0 -0
- /package/{AdoptionSpecialist.ouro → SerpentGuide.ouro}/psyche/identities/jafar.md +0 -0
- /package/{AdoptionSpecialist.ouro → SerpentGuide.ouro}/psyche/identities/jormungandr.md +0 -0
- /package/{AdoptionSpecialist.ouro → SerpentGuide.ouro}/psyche/identities/kaa.md +0 -0
- /package/{AdoptionSpecialist.ouro → SerpentGuide.ouro}/psyche/identities/medusa.md +0 -0
- /package/{AdoptionSpecialist.ouro → SerpentGuide.ouro}/psyche/identities/monty.md +0 -0
- /package/{AdoptionSpecialist.ouro → SerpentGuide.ouro}/psyche/identities/nagini.md +0 -0
- /package/{AdoptionSpecialist.ouro → SerpentGuide.ouro}/psyche/identities/ouroboros.md +0 -0
- /package/{AdoptionSpecialist.ouro → SerpentGuide.ouro}/psyche/identities/python.md +0 -0
- /package/{AdoptionSpecialist.ouro → SerpentGuide.ouro}/psyche/identities/quetzalcoatl.md +0 -0
- /package/{AdoptionSpecialist.ouro → SerpentGuide.ouro}/psyche/identities/sir-hiss.md +0 -0
- /package/{AdoptionSpecialist.ouro → SerpentGuide.ouro}/psyche/identities/the-snake.md +0 -0
- /package/dist/heart/{daemon → hatch}/hatch-animation.js +0 -0
- /package/dist/heart/{daemon → hatch}/specialist-orchestrator.js +0 -0
- /package/dist/heart/{daemon → versioning}/ouro-uti.js +0 -0
- /package/dist/heart/{daemon → versioning}/wrapper-publish-guard.js +0 -0
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
<!doctype html>
|
|
2
|
+
<html lang="en" class="dark">
|
|
3
|
+
<head>
|
|
4
|
+
<meta charset="utf-8" />
|
|
5
|
+
<meta name="viewport" content="width=device-width, initial-scale=1" />
|
|
6
|
+
<meta name="color-scheme" content="dark" />
|
|
7
|
+
<title>Ouro Outlook</title>
|
|
8
|
+
<meta name="description" content="The daemon-hosted shared orientation surface for agents alive on this machine." />
|
|
9
|
+
<script type="module" crossorigin src="/assets/index-D7l3l4vY.js"></script>
|
|
10
|
+
<link rel="stylesheet" crossorigin href="/assets/index-BAcU08c-.css">
|
|
11
|
+
</head>
|
|
12
|
+
<body>
|
|
13
|
+
<div id="app"></div>
|
|
14
|
+
</body>
|
|
15
|
+
</html>
|
|
@@ -9,6 +9,7 @@ exports.discoverOrganizations = discoverOrganizations;
|
|
|
9
9
|
exports.discoverProjects = discoverProjects;
|
|
10
10
|
const api_error_1 = require("../heart/api-error");
|
|
11
11
|
const runtime_1 = require("../nerves/runtime");
|
|
12
|
+
const api_client_1 = require("./api-client");
|
|
12
13
|
const ADO_BASE = "https://dev.azure.com";
|
|
13
14
|
const VSSPS_BASE = "https://app.vssps.visualstudio.com";
|
|
14
15
|
const DEFAULT_API_VERSION = "api-version=7.1";
|
|
@@ -30,62 +31,20 @@ function resolveContentType(method, path) {
|
|
|
30
31
|
// Generic ADO API request. Returns response body as pretty-printed JSON string.
|
|
31
32
|
// `host` overrides the base URL for non-standard APIs (e.g. "vsapm.dev.azure.com", "vssps.dev.azure.com").
|
|
32
33
|
async function adoRequest(token, method, org, path, body, host) {
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
Authorization: `Bearer ${token}`,
|
|
48
|
-
"Content-Type": contentType,
|
|
49
|
-
},
|
|
50
|
-
};
|
|
51
|
-
if (body)
|
|
52
|
-
opts.body = body;
|
|
53
|
-
const res = await fetch(url, opts);
|
|
54
|
-
if (!res.ok) {
|
|
55
|
-
(0, runtime_1.emitNervesEvent)({
|
|
56
|
-
level: "error",
|
|
57
|
-
event: "client.error",
|
|
58
|
-
component: "clients",
|
|
59
|
-
message: "ADO request failed",
|
|
60
|
-
meta: { client: "ado", method, org, path, status: res.status },
|
|
61
|
-
});
|
|
62
|
-
return (0, api_error_1.handleApiError)(res, "ADO", "ado");
|
|
63
|
-
}
|
|
64
|
-
const data = await res.json();
|
|
65
|
-
(0, runtime_1.emitNervesEvent)({
|
|
66
|
-
event: "client.request_end",
|
|
67
|
-
component: "clients",
|
|
68
|
-
message: "ADO request completed",
|
|
69
|
-
meta: { client: "ado", method, org, path, success: true },
|
|
70
|
-
});
|
|
71
|
-
return JSON.stringify(data, null, 2);
|
|
72
|
-
}
|
|
73
|
-
catch (err) {
|
|
74
|
-
(0, runtime_1.emitNervesEvent)({
|
|
75
|
-
level: "error",
|
|
76
|
-
event: "client.error",
|
|
77
|
-
component: "clients",
|
|
78
|
-
message: "ADO request threw exception",
|
|
79
|
-
meta: {
|
|
80
|
-
client: "ado",
|
|
81
|
-
method,
|
|
82
|
-
org,
|
|
83
|
-
path,
|
|
84
|
-
reason: err instanceof Error ? err.message : String(err),
|
|
85
|
-
},
|
|
86
|
-
});
|
|
87
|
-
return (0, api_error_1.handleApiError)(err, "ADO", "ado");
|
|
88
|
-
}
|
|
34
|
+
const base = host ? `https://${host}/${org}` : `${ADO_BASE}/${org}`;
|
|
35
|
+
const fullPath = ensureApiVersion(path);
|
|
36
|
+
return (0, api_client_1.apiRequest)({
|
|
37
|
+
baseUrl: base,
|
|
38
|
+
method,
|
|
39
|
+
path: fullPath,
|
|
40
|
+
token,
|
|
41
|
+
clientName: "ado",
|
|
42
|
+
serviceLabel: "ADO",
|
|
43
|
+
connectionName: "ado",
|
|
44
|
+
body,
|
|
45
|
+
contentType: resolveContentType(method, path),
|
|
46
|
+
eventMeta: { org },
|
|
47
|
+
});
|
|
89
48
|
}
|
|
90
49
|
// Backward-compatible thin wrapper: runs WIQL query and returns formatted work items.
|
|
91
50
|
async function queryWorkItems(token, org, query) {
|
|
@@ -221,7 +221,7 @@ exports.adoSemanticToolDefinitions = [
|
|
|
221
221
|
type: "function",
|
|
222
222
|
function: {
|
|
223
223
|
name: "ado_backlog_list",
|
|
224
|
-
description: "Query the backlog and return enriched work items with hierarchy, type, parent, assignee, area path, and iteration. Supports filtering. Use this instead of raw WIQL queries.",
|
|
224
|
+
description: "Query the backlog and return enriched work items with hierarchy, type, parent, assignee, area path, and iteration. Supports filtering. Use this instead of raw WIQL queries. Provides enriched results with hierarchy.",
|
|
225
225
|
parameters: {
|
|
226
226
|
type: "object",
|
|
227
227
|
properties: {
|
|
@@ -279,6 +279,7 @@ exports.adoSemanticToolDefinitions = [
|
|
|
279
279
|
return formatForChannel(items, ctx, organization, project);
|
|
280
280
|
},
|
|
281
281
|
integration: "ado",
|
|
282
|
+
summaryKeys: ["organization", "project"],
|
|
282
283
|
},
|
|
283
284
|
// -- ado_create_epic --
|
|
284
285
|
{
|
|
@@ -286,7 +287,7 @@ exports.adoSemanticToolDefinitions = [
|
|
|
286
287
|
type: "function",
|
|
287
288
|
function: {
|
|
288
289
|
name: "ado_create_epic",
|
|
289
|
-
description: "Create an epic in Azure DevOps with title, description, area path, and optional parent.",
|
|
290
|
+
description: "Create an epic in Azure DevOps with title, description, area path, and optional parent. Use ado_preview_changes first to dry-run the operation.",
|
|
290
291
|
parameters: {
|
|
291
292
|
type: "object",
|
|
292
293
|
properties: {
|
|
@@ -317,7 +318,7 @@ exports.adoSemanticToolDefinitions = [
|
|
|
317
318
|
return result;
|
|
318
319
|
},
|
|
319
320
|
integration: "ado",
|
|
320
|
-
|
|
321
|
+
summaryKeys: ["organization", "project", "title"],
|
|
321
322
|
},
|
|
322
323
|
// -- ado_create_issue --
|
|
323
324
|
{
|
|
@@ -325,7 +326,7 @@ exports.adoSemanticToolDefinitions = [
|
|
|
325
326
|
type: "function",
|
|
326
327
|
function: {
|
|
327
328
|
name: "ado_create_issue",
|
|
328
|
-
description: "Create an issue or user story in Azure DevOps with title, description, area path, and parent epic.",
|
|
329
|
+
description: "Create an issue or user story in Azure DevOps with title, description, area path, and parent epic. Use ado_preview_changes first to dry-run the operation.",
|
|
329
330
|
parameters: {
|
|
330
331
|
type: "object",
|
|
331
332
|
properties: {
|
|
@@ -358,7 +359,7 @@ exports.adoSemanticToolDefinitions = [
|
|
|
358
359
|
return result;
|
|
359
360
|
},
|
|
360
361
|
integration: "ado",
|
|
361
|
-
|
|
362
|
+
summaryKeys: ["organization", "project", "title"],
|
|
362
363
|
},
|
|
363
364
|
// -- ado_move_items --
|
|
364
365
|
{
|
|
@@ -366,7 +367,7 @@ exports.adoSemanticToolDefinitions = [
|
|
|
366
367
|
type: "function",
|
|
367
368
|
function: {
|
|
368
369
|
name: "ado_move_items",
|
|
369
|
-
description: "Reparent work items -- move them to a new parent epic or feature.",
|
|
370
|
+
description: "Reparent work items -- move them to a new parent epic or feature. Use ado_preview_changes first. These mutations affect real work items.",
|
|
370
371
|
parameters: {
|
|
371
372
|
type: "object",
|
|
372
373
|
properties: {
|
|
@@ -412,7 +413,7 @@ exports.adoSemanticToolDefinitions = [
|
|
|
412
413
|
return JSON.stringify({ moved, errors, organization: adoCtx.organization, project: adoCtx.project });
|
|
413
414
|
},
|
|
414
415
|
integration: "ado",
|
|
415
|
-
|
|
416
|
+
summaryKeys: ["organization", "project", "workItemIds"],
|
|
416
417
|
},
|
|
417
418
|
// -- ado_restructure_backlog --
|
|
418
419
|
{
|
|
@@ -420,7 +421,7 @@ exports.adoSemanticToolDefinitions = [
|
|
|
420
421
|
type: "function",
|
|
421
422
|
function: {
|
|
422
423
|
name: "ado_restructure_backlog",
|
|
423
|
-
description: "Bulk restructure: reparent multiple work items in a single logical operation.",
|
|
424
|
+
description: "Bulk restructure: reparent multiple work items in a single logical operation. Use ado_preview_changes first. These mutations affect real work items.",
|
|
424
425
|
parameters: {
|
|
425
426
|
type: "object",
|
|
426
427
|
properties: {
|
|
@@ -470,7 +471,7 @@ exports.adoSemanticToolDefinitions = [
|
|
|
470
471
|
return JSON.stringify({ results, organization: adoCtx.organization, project: adoCtx.project });
|
|
471
472
|
},
|
|
472
473
|
integration: "ado",
|
|
473
|
-
|
|
474
|
+
summaryKeys: ["organization", "project"],
|
|
474
475
|
},
|
|
475
476
|
// -- ado_validate_structure --
|
|
476
477
|
{
|
|
@@ -671,7 +672,7 @@ exports.adoSemanticToolDefinitions = [
|
|
|
671
672
|
return JSON.stringify({ results, organization: adoCtx.organization, project: adoCtx.project });
|
|
672
673
|
},
|
|
673
674
|
integration: "ado",
|
|
674
|
-
|
|
675
|
+
summaryKeys: ["organization", "project"],
|
|
675
676
|
},
|
|
676
677
|
// -- ado_detect_orphans --
|
|
677
678
|
{
|
|
@@ -0,0 +1,97 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
/**
|
|
3
|
+
* Shared HTTP API client for Graph, ADO, and GitHub clients.
|
|
4
|
+
*
|
|
5
|
+
* All three API clients follow the same pattern: emit request_start event,
|
|
6
|
+
* build request with auth header, fetch, handle errors, emit request_end,
|
|
7
|
+
* and return pretty-printed JSON. This module extracts that shared logic.
|
|
8
|
+
*/
|
|
9
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
10
|
+
exports.apiRequest = apiRequest;
|
|
11
|
+
const api_error_1 = require("../heart/api-error");
|
|
12
|
+
const runtime_1 = require("../nerves/runtime");
|
|
13
|
+
const credential_access_1 = require("./credential-access");
|
|
14
|
+
/**
|
|
15
|
+
* Execute an authenticated API request with standard logging and error handling.
|
|
16
|
+
* Returns the response body as a pretty-printed JSON string.
|
|
17
|
+
*/
|
|
18
|
+
async function apiRequest(options) {
|
|
19
|
+
const { baseUrl, method, path, token, clientName, serviceLabel, connectionName, body, extraHeaders, contentType, eventMeta, credentialDomain, credentialField, } = options;
|
|
20
|
+
// Resolve token from credential store if credentialDomain is provided
|
|
21
|
+
let resolvedToken = token;
|
|
22
|
+
if (credentialDomain) {
|
|
23
|
+
try {
|
|
24
|
+
const store = (0, credential_access_1.getCredentialStore)();
|
|
25
|
+
resolvedToken = await store.getRawSecret(credentialDomain, credentialField ?? "password");
|
|
26
|
+
(0, runtime_1.emitNervesEvent)({
|
|
27
|
+
event: "client.credential_fetch",
|
|
28
|
+
component: "clients",
|
|
29
|
+
message: `fetched credential from store for ${serviceLabel}`,
|
|
30
|
+
meta: { client: clientName, credentialDomain },
|
|
31
|
+
});
|
|
32
|
+
}
|
|
33
|
+
catch (err) {
|
|
34
|
+
(0, runtime_1.emitNervesEvent)({
|
|
35
|
+
level: "error",
|
|
36
|
+
event: "client.credential_error",
|
|
37
|
+
component: "clients",
|
|
38
|
+
message: `failed to fetch credential for ${serviceLabel}`,
|
|
39
|
+
/* v8 ignore next -- defensive: getRawSecret throws Error instances @preserve */
|
|
40
|
+
meta: { client: clientName, credentialDomain, reason: err instanceof Error ? err.message : String(err) },
|
|
41
|
+
});
|
|
42
|
+
return (0, api_error_1.handleApiError)(err, serviceLabel, connectionName);
|
|
43
|
+
}
|
|
44
|
+
}
|
|
45
|
+
try {
|
|
46
|
+
(0, runtime_1.emitNervesEvent)({
|
|
47
|
+
event: "client.request_start",
|
|
48
|
+
component: "clients",
|
|
49
|
+
message: `starting ${serviceLabel} request`,
|
|
50
|
+
meta: { client: clientName, method, path, ...eventMeta },
|
|
51
|
+
});
|
|
52
|
+
const url = `${baseUrl}${path}`;
|
|
53
|
+
const headers = {
|
|
54
|
+
Authorization: `Bearer ${resolvedToken}`,
|
|
55
|
+
"Content-Type": contentType ?? "application/json",
|
|
56
|
+
...extraHeaders,
|
|
57
|
+
};
|
|
58
|
+
const opts = { method, headers };
|
|
59
|
+
if (body)
|
|
60
|
+
opts.body = body;
|
|
61
|
+
const res = await fetch(url, opts);
|
|
62
|
+
if (!res.ok) {
|
|
63
|
+
(0, runtime_1.emitNervesEvent)({
|
|
64
|
+
level: "error",
|
|
65
|
+
event: "client.error",
|
|
66
|
+
component: "clients",
|
|
67
|
+
message: `${serviceLabel} request failed`,
|
|
68
|
+
meta: { client: clientName, method, path, status: res.status, ...eventMeta },
|
|
69
|
+
});
|
|
70
|
+
return (0, api_error_1.handleApiError)(res, serviceLabel, connectionName);
|
|
71
|
+
}
|
|
72
|
+
const data = await res.json();
|
|
73
|
+
(0, runtime_1.emitNervesEvent)({
|
|
74
|
+
event: "client.request_end",
|
|
75
|
+
component: "clients",
|
|
76
|
+
message: `${serviceLabel} request completed`,
|
|
77
|
+
meta: { client: clientName, method, path, success: true, ...eventMeta },
|
|
78
|
+
});
|
|
79
|
+
return JSON.stringify(data, null, 2);
|
|
80
|
+
}
|
|
81
|
+
catch (err) {
|
|
82
|
+
(0, runtime_1.emitNervesEvent)({
|
|
83
|
+
level: "error",
|
|
84
|
+
event: "client.error",
|
|
85
|
+
component: "clients",
|
|
86
|
+
message: `${serviceLabel} request threw exception`,
|
|
87
|
+
meta: {
|
|
88
|
+
client: clientName,
|
|
89
|
+
method,
|
|
90
|
+
path,
|
|
91
|
+
reason: err instanceof Error ? err.message : String(err),
|
|
92
|
+
...eventMeta,
|
|
93
|
+
},
|
|
94
|
+
});
|
|
95
|
+
return (0, api_error_1.handleApiError)(err, serviceLabel, connectionName);
|
|
96
|
+
}
|
|
97
|
+
}
|
|
@@ -0,0 +1,365 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
/**
|
|
3
|
+
* Bitwarden CLI credential store — wraps `bw` CLI for the agent's own vault.
|
|
4
|
+
*
|
|
5
|
+
* This store authenticates directly as the agent using its own master password.
|
|
6
|
+
* The agent owns the vault, so no human-in-the-loop is needed.
|
|
7
|
+
*
|
|
8
|
+
* Requires the `bw` CLI to be installed. Session tokens are cached process-local.
|
|
9
|
+
*/
|
|
10
|
+
var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
|
|
11
|
+
if (k2 === undefined) k2 = k;
|
|
12
|
+
var desc = Object.getOwnPropertyDescriptor(m, k);
|
|
13
|
+
if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
|
|
14
|
+
desc = { enumerable: true, get: function() { return m[k]; } };
|
|
15
|
+
}
|
|
16
|
+
Object.defineProperty(o, k2, desc);
|
|
17
|
+
}) : (function(o, m, k, k2) {
|
|
18
|
+
if (k2 === undefined) k2 = k;
|
|
19
|
+
o[k2] = m[k];
|
|
20
|
+
}));
|
|
21
|
+
var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
|
|
22
|
+
Object.defineProperty(o, "default", { enumerable: true, value: v });
|
|
23
|
+
}) : function(o, v) {
|
|
24
|
+
o["default"] = v;
|
|
25
|
+
});
|
|
26
|
+
var __importStar = (this && this.__importStar) || (function () {
|
|
27
|
+
var ownKeys = function(o) {
|
|
28
|
+
ownKeys = Object.getOwnPropertyNames || function (o) {
|
|
29
|
+
var ar = [];
|
|
30
|
+
for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k;
|
|
31
|
+
return ar;
|
|
32
|
+
};
|
|
33
|
+
return ownKeys(o);
|
|
34
|
+
};
|
|
35
|
+
return function (mod) {
|
|
36
|
+
if (mod && mod.__esModule) return mod;
|
|
37
|
+
var result = {};
|
|
38
|
+
if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]);
|
|
39
|
+
__setModuleDefault(result, mod);
|
|
40
|
+
return result;
|
|
41
|
+
};
|
|
42
|
+
})();
|
|
43
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
44
|
+
exports.BitwardenCredentialStore = void 0;
|
|
45
|
+
const node_child_process_1 = require("node:child_process");
|
|
46
|
+
const fs = __importStar(require("node:fs"));
|
|
47
|
+
const runtime_1 = require("../nerves/runtime");
|
|
48
|
+
const bw_installer_1 = require("./bw-installer");
|
|
49
|
+
// ---------------------------------------------------------------------------
|
|
50
|
+
// bw CLI wrapper
|
|
51
|
+
// ---------------------------------------------------------------------------
|
|
52
|
+
function execBw(args, sessionToken, appDataDir) {
|
|
53
|
+
const env = {
|
|
54
|
+
...process.env,
|
|
55
|
+
...(sessionToken ? { BW_SESSION: sessionToken } : {}),
|
|
56
|
+
...(appDataDir ? { BITWARDENCLI_APPDATA_DIR: appDataDir } : {}),
|
|
57
|
+
};
|
|
58
|
+
return new Promise((resolve, reject) => {
|
|
59
|
+
(0, node_child_process_1.execFile)("bw", args, { timeout: 30_000, env }, (err, stdout) => {
|
|
60
|
+
if (err) {
|
|
61
|
+
if (isBwNotInstalled(err)) {
|
|
62
|
+
reject(new Error("bw CLI not found. Install from https://bitwarden.com/help/cli/"));
|
|
63
|
+
return;
|
|
64
|
+
}
|
|
65
|
+
reject(new Error(`bw CLI error: ${err.message}`));
|
|
66
|
+
return;
|
|
67
|
+
}
|
|
68
|
+
resolve(stdout);
|
|
69
|
+
});
|
|
70
|
+
});
|
|
71
|
+
}
|
|
72
|
+
/** Check if the error indicates the bw CLI binary is not installed. */
|
|
73
|
+
function isBwNotInstalled(err) {
|
|
74
|
+
const msg = err.message.toLowerCase();
|
|
75
|
+
const code = err.code;
|
|
76
|
+
return code === "ENOENT" || msg.includes("enoent") || msg.includes("not found") || msg.includes("command not found");
|
|
77
|
+
}
|
|
78
|
+
/** Check if the error is transient (network/timeout) and worth retrying. */
|
|
79
|
+
function isTransientError(err) {
|
|
80
|
+
const msg = err.message.toLowerCase();
|
|
81
|
+
return (msg.includes("econnrefused") ||
|
|
82
|
+
msg.includes("etimedout") ||
|
|
83
|
+
msg.includes("enotfound") ||
|
|
84
|
+
msg.includes("socket hang up") ||
|
|
85
|
+
msg.includes("503") ||
|
|
86
|
+
msg.includes("server unavailable"));
|
|
87
|
+
}
|
|
88
|
+
const MAX_RETRIES = 3;
|
|
89
|
+
const BASE_BACKOFF_MS = 1000;
|
|
90
|
+
function delay(ms) {
|
|
91
|
+
return new Promise((resolve) => setTimeout(resolve, ms));
|
|
92
|
+
}
|
|
93
|
+
// ---------------------------------------------------------------------------
|
|
94
|
+
// BitwardenCredentialStore
|
|
95
|
+
// ---------------------------------------------------------------------------
|
|
96
|
+
class BitwardenCredentialStore {
|
|
97
|
+
serverUrl;
|
|
98
|
+
email;
|
|
99
|
+
masterPassword;
|
|
100
|
+
appDataDir;
|
|
101
|
+
sessionToken = null;
|
|
102
|
+
constructor(serverUrl, email, masterPassword, options = {}) {
|
|
103
|
+
this.serverUrl = serverUrl;
|
|
104
|
+
this.email = email;
|
|
105
|
+
this.masterPassword = masterPassword;
|
|
106
|
+
this.appDataDir = options.appDataDir;
|
|
107
|
+
}
|
|
108
|
+
isReady() {
|
|
109
|
+
return true;
|
|
110
|
+
}
|
|
111
|
+
/**
|
|
112
|
+
* Ensure the bw CLI is authenticated and unlocked.
|
|
113
|
+
* Handles three states: logged out → login, locked → unlock, already unlocked → no-op.
|
|
114
|
+
* Retries transient failures (network/timeout) up to MAX_RETRIES with exponential backoff.
|
|
115
|
+
*/
|
|
116
|
+
async login() {
|
|
117
|
+
// Ensure bw CLI is installed before any bw commands
|
|
118
|
+
await (0, bw_installer_1.ensureBwCli)();
|
|
119
|
+
if (this.appDataDir) {
|
|
120
|
+
fs.mkdirSync(this.appDataDir, { recursive: true, mode: 0o700 });
|
|
121
|
+
}
|
|
122
|
+
let lastError;
|
|
123
|
+
for (let attempt = 0; attempt < MAX_RETRIES; attempt++) {
|
|
124
|
+
try {
|
|
125
|
+
await this.loginAttempt();
|
|
126
|
+
return;
|
|
127
|
+
}
|
|
128
|
+
catch (err) {
|
|
129
|
+
/* v8 ignore next -- defensive: loginAttempt always throws Error instances @preserve */
|
|
130
|
+
lastError = err instanceof Error ? err : new Error(String(err));
|
|
131
|
+
// Don't retry non-transient errors (auth failures, bw not installed)
|
|
132
|
+
if (!isTransientError(lastError)) {
|
|
133
|
+
throw lastError;
|
|
134
|
+
}
|
|
135
|
+
// Don't retry after final attempt
|
|
136
|
+
if (attempt === MAX_RETRIES - 1)
|
|
137
|
+
break;
|
|
138
|
+
const backoffMs = BASE_BACKOFF_MS * Math.pow(2, attempt);
|
|
139
|
+
(0, runtime_1.emitNervesEvent)({
|
|
140
|
+
event: "repertoire.bw_login_retry",
|
|
141
|
+
component: "repertoire",
|
|
142
|
+
message: `bw login attempt ${attempt + 1} failed, retrying in ${backoffMs}ms`,
|
|
143
|
+
meta: { attempt: attempt + 1, backoffMs, reason: lastError.message },
|
|
144
|
+
});
|
|
145
|
+
await delay(backoffMs);
|
|
146
|
+
}
|
|
147
|
+
}
|
|
148
|
+
throw lastError;
|
|
149
|
+
}
|
|
150
|
+
/** Single login attempt — called by login() retry loop. */
|
|
151
|
+
async loginAttempt() {
|
|
152
|
+
// Check current status
|
|
153
|
+
let status = {};
|
|
154
|
+
try {
|
|
155
|
+
const raw = await execBw(["status"], undefined, this.appDataDir);
|
|
156
|
+
status = JSON.parse(raw);
|
|
157
|
+
}
|
|
158
|
+
catch (err) {
|
|
159
|
+
// If bw CLI is not installed or a transient error, propagate it for retry
|
|
160
|
+
if (err instanceof Error && (isBwNotInstalled(err) || isTransientError(err))) {
|
|
161
|
+
throw err;
|
|
162
|
+
}
|
|
163
|
+
// CLI not configured or broken — proceed with full setup
|
|
164
|
+
}
|
|
165
|
+
// Configure server URL if needed (only works when logged out)
|
|
166
|
+
if (status.status === "unauthenticated" || !status.serverUrl) {
|
|
167
|
+
try {
|
|
168
|
+
await execBw(["config", "server", this.serverUrl], undefined, this.appDataDir);
|
|
169
|
+
}
|
|
170
|
+
catch {
|
|
171
|
+
// "Logout required" means already logged in — that's fine, skip config
|
|
172
|
+
}
|
|
173
|
+
}
|
|
174
|
+
if (status.status === "locked") {
|
|
175
|
+
// Already logged in, just needs unlock
|
|
176
|
+
const unlockOutput = await execBw(["unlock", this.masterPassword, "--raw"], undefined, this.appDataDir);
|
|
177
|
+
this.sessionToken = unlockOutput.trim();
|
|
178
|
+
}
|
|
179
|
+
else if (status.status === "unauthenticated" || !status.status) {
|
|
180
|
+
// Not logged in — full login
|
|
181
|
+
const loginOutput = await execBw(["login", this.email, this.masterPassword, "--raw"], undefined, this.appDataDir);
|
|
182
|
+
try {
|
|
183
|
+
const parsed = JSON.parse(loginOutput);
|
|
184
|
+
this.sessionToken = parsed.access_token ?? loginOutput.trim();
|
|
185
|
+
}
|
|
186
|
+
catch {
|
|
187
|
+
this.sessionToken = loginOutput.trim();
|
|
188
|
+
}
|
|
189
|
+
}
|
|
190
|
+
else {
|
|
191
|
+
// Status is "unlocked" — already good, just need the session token
|
|
192
|
+
const unlockOutput = await execBw(["unlock", this.masterPassword, "--raw"], undefined, this.appDataDir);
|
|
193
|
+
this.sessionToken = unlockOutput.trim();
|
|
194
|
+
}
|
|
195
|
+
}
|
|
196
|
+
async ensureSession() {
|
|
197
|
+
if (!this.sessionToken) {
|
|
198
|
+
await this.login();
|
|
199
|
+
}
|
|
200
|
+
/* v8 ignore next -- defensive: login() always sets sessionToken on success @preserve */
|
|
201
|
+
return this.sessionToken ?? undefined;
|
|
202
|
+
}
|
|
203
|
+
async get(domain) {
|
|
204
|
+
(0, runtime_1.emitNervesEvent)({
|
|
205
|
+
event: "repertoire.bw_credential_get_start",
|
|
206
|
+
component: "repertoire",
|
|
207
|
+
message: `getting credential via bw for ${domain}`,
|
|
208
|
+
meta: { domain, backend: "bitwarden" },
|
|
209
|
+
});
|
|
210
|
+
const session = await this.ensureSession();
|
|
211
|
+
const item = await this.findItemByDomain(domain, session);
|
|
212
|
+
if (!item) {
|
|
213
|
+
(0, runtime_1.emitNervesEvent)({
|
|
214
|
+
event: "repertoire.bw_credential_get_end",
|
|
215
|
+
component: "repertoire",
|
|
216
|
+
message: `no bw credential for ${domain}`,
|
|
217
|
+
meta: { domain, found: false, backend: "bitwarden" },
|
|
218
|
+
});
|
|
219
|
+
return null;
|
|
220
|
+
}
|
|
221
|
+
(0, runtime_1.emitNervesEvent)({
|
|
222
|
+
event: "repertoire.bw_credential_get_end",
|
|
223
|
+
component: "repertoire",
|
|
224
|
+
message: `bw credential found for ${domain}`,
|
|
225
|
+
meta: { domain, found: true, backend: "bitwarden" },
|
|
226
|
+
});
|
|
227
|
+
return {
|
|
228
|
+
domain: item.name,
|
|
229
|
+
username: item.login?.username,
|
|
230
|
+
notes: item.notes ?? undefined,
|
|
231
|
+
createdAt: item.revisionDate ?? new Date().toISOString(),
|
|
232
|
+
};
|
|
233
|
+
}
|
|
234
|
+
async getRawSecret(domain, field) {
|
|
235
|
+
const session = await this.ensureSession();
|
|
236
|
+
const item = await this.findItemByDomain(domain, session);
|
|
237
|
+
if (!item) {
|
|
238
|
+
throw new Error(`no credential found for domain "${domain}"`);
|
|
239
|
+
}
|
|
240
|
+
// Map common field names to bw item structure
|
|
241
|
+
let value;
|
|
242
|
+
if (field === "password") {
|
|
243
|
+
value = item.login?.password;
|
|
244
|
+
}
|
|
245
|
+
else if (field === "username") {
|
|
246
|
+
value = item.login?.username;
|
|
247
|
+
}
|
|
248
|
+
else {
|
|
249
|
+
value = item[field];
|
|
250
|
+
}
|
|
251
|
+
if (value === undefined || value === null) {
|
|
252
|
+
throw new Error(`field "${field}" not found for domain "${domain}"`);
|
|
253
|
+
}
|
|
254
|
+
return String(value);
|
|
255
|
+
}
|
|
256
|
+
async store(domain, data) {
|
|
257
|
+
(0, runtime_1.emitNervesEvent)({
|
|
258
|
+
event: "repertoire.bw_credential_store_start",
|
|
259
|
+
component: "repertoire",
|
|
260
|
+
message: `storing credential via bw for ${domain}`,
|
|
261
|
+
meta: { domain, backend: "bitwarden" },
|
|
262
|
+
});
|
|
263
|
+
const session = await this.ensureSession();
|
|
264
|
+
const existing = await this.findItemByDomain(domain, session);
|
|
265
|
+
const item = {
|
|
266
|
+
...(existing ?? {}),
|
|
267
|
+
type: 1, // Login type
|
|
268
|
+
name: domain,
|
|
269
|
+
login: {
|
|
270
|
+
username: data.username ?? "",
|
|
271
|
+
password: data.password,
|
|
272
|
+
uris: [{ match: null, uri: `https://${domain}` }],
|
|
273
|
+
},
|
|
274
|
+
notes: data.notes ?? null,
|
|
275
|
+
};
|
|
276
|
+
const encoded = Buffer.from(JSON.stringify(item)).toString("base64");
|
|
277
|
+
if (existing) {
|
|
278
|
+
await execBw(["edit", "item", existing.id, encoded], session, this.appDataDir);
|
|
279
|
+
}
|
|
280
|
+
else {
|
|
281
|
+
await execBw(["create", "item", encoded], session, this.appDataDir);
|
|
282
|
+
}
|
|
283
|
+
(0, runtime_1.emitNervesEvent)({
|
|
284
|
+
event: "repertoire.bw_credential_store_end",
|
|
285
|
+
component: "repertoire",
|
|
286
|
+
message: `credential stored via bw for ${domain}`,
|
|
287
|
+
meta: { domain, backend: "bitwarden" },
|
|
288
|
+
});
|
|
289
|
+
}
|
|
290
|
+
async list() {
|
|
291
|
+
(0, runtime_1.emitNervesEvent)({
|
|
292
|
+
event: "repertoire.bw_credential_list_start",
|
|
293
|
+
component: "repertoire",
|
|
294
|
+
message: "listing bw credentials",
|
|
295
|
+
meta: { backend: "bitwarden" },
|
|
296
|
+
});
|
|
297
|
+
const session = await this.ensureSession();
|
|
298
|
+
try {
|
|
299
|
+
const stdout = await execBw(["list", "items"], session, this.appDataDir);
|
|
300
|
+
const items = JSON.parse(stdout);
|
|
301
|
+
const results = items.map((item) => ({
|
|
302
|
+
domain: item.name,
|
|
303
|
+
username: item.login?.username,
|
|
304
|
+
notes: item.notes ?? undefined,
|
|
305
|
+
createdAt: item.revisionDate ?? new Date().toISOString(),
|
|
306
|
+
}));
|
|
307
|
+
(0, runtime_1.emitNervesEvent)({
|
|
308
|
+
event: "repertoire.bw_credential_list_end",
|
|
309
|
+
component: "repertoire",
|
|
310
|
+
message: "bw credentials listed",
|
|
311
|
+
meta: { backend: "bitwarden", count: results.length },
|
|
312
|
+
});
|
|
313
|
+
return results;
|
|
314
|
+
}
|
|
315
|
+
catch {
|
|
316
|
+
(0, runtime_1.emitNervesEvent)({
|
|
317
|
+
event: "repertoire.bw_credential_list_end",
|
|
318
|
+
component: "repertoire",
|
|
319
|
+
message: "bw credential list failed",
|
|
320
|
+
meta: { backend: "bitwarden", count: 0 },
|
|
321
|
+
});
|
|
322
|
+
return [];
|
|
323
|
+
}
|
|
324
|
+
}
|
|
325
|
+
async delete(domain) {
|
|
326
|
+
(0, runtime_1.emitNervesEvent)({
|
|
327
|
+
event: "repertoire.bw_credential_delete_start",
|
|
328
|
+
component: "repertoire",
|
|
329
|
+
message: `deleting credential via bw for ${domain}`,
|
|
330
|
+
meta: { domain, backend: "bitwarden" },
|
|
331
|
+
});
|
|
332
|
+
const session = await this.ensureSession();
|
|
333
|
+
const item = await this.findItemByDomain(domain, session);
|
|
334
|
+
if (!item) {
|
|
335
|
+
(0, runtime_1.emitNervesEvent)({
|
|
336
|
+
event: "repertoire.bw_credential_delete_end",
|
|
337
|
+
component: "repertoire",
|
|
338
|
+
message: `no bw credential to delete for ${domain}`,
|
|
339
|
+
meta: { domain, deleted: false, backend: "bitwarden" },
|
|
340
|
+
});
|
|
341
|
+
return false;
|
|
342
|
+
}
|
|
343
|
+
await execBw(["delete", "item", item.id], session, this.appDataDir);
|
|
344
|
+
(0, runtime_1.emitNervesEvent)({
|
|
345
|
+
event: "repertoire.bw_credential_delete_end",
|
|
346
|
+
component: "repertoire",
|
|
347
|
+
message: `credential deleted via bw for ${domain}`,
|
|
348
|
+
meta: { domain, deleted: true, backend: "bitwarden" },
|
|
349
|
+
});
|
|
350
|
+
return true;
|
|
351
|
+
}
|
|
352
|
+
// --- Private ---
|
|
353
|
+
async findItemByDomain(domain, session) {
|
|
354
|
+
try {
|
|
355
|
+
const stdout = await execBw(["list", "items", "--search", domain], session, this.appDataDir);
|
|
356
|
+
const items = JSON.parse(stdout);
|
|
357
|
+
// Find exact match by name
|
|
358
|
+
return items.find((item) => item.name === domain) ?? items[0] ?? null;
|
|
359
|
+
}
|
|
360
|
+
catch {
|
|
361
|
+
return null;
|
|
362
|
+
}
|
|
363
|
+
}
|
|
364
|
+
}
|
|
365
|
+
exports.BitwardenCredentialStore = BitwardenCredentialStore;
|