@unerr-ai/unerr 0.2.1 → 0.2.3
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 +36 -45
- package/dist/cli.js +37443 -36022
- package/package.json +2 -1
- package/dist/behaviors/agent-llm-bridge.js +0 -166
- package/dist/behaviors/architecture-guard.js +0 -256
- package/dist/behaviors/auto-doc.js +0 -247
- package/dist/behaviors/cascade-guard.js +0 -289
- package/dist/behaviors/change-narrative.js +0 -270
- package/dist/behaviors/convention-drift.js +0 -290
- package/dist/behaviors/framework.js +0 -235
- package/dist/behaviors/guard-formatter.js +0 -44
- package/dist/behaviors/incomplete-work.js +0 -270
- package/dist/behaviors/loop-breaker.js +0 -300
- package/dist/behaviors/session-continuity.js +0 -208
- package/dist/commands/branches.js +0 -97
- package/dist/commands/check-commit.js +0 -225
- package/dist/commands/compress-output.js +0 -64
- package/dist/commands/config-verify.js +0 -243
- package/dist/commands/daemon.js +0 -905
- package/dist/commands/dashboard.js +0 -52
- package/dist/commands/debug.js +0 -200
- package/dist/commands/enrich.js +0 -184
- package/dist/commands/exec.js +0 -233
- package/dist/commands/gain.js +0 -156
- package/dist/commands/hook.js +0 -88
- package/dist/commands/index.js +0 -88
- package/dist/commands/init.js +0 -74
- package/dist/commands/install.js +0 -505
- package/dist/commands/learn.js +0 -116
- package/dist/commands/manifest.js +0 -193
- package/dist/commands/rewind.js +0 -103
- package/dist/commands/serve.js +0 -19
- package/dist/commands/setup-wizard.js +0 -414
- package/dist/commands/skills.js +0 -64
- package/dist/commands/stats.js +0 -20
- package/dist/commands/status.js +0 -654
- package/dist/commands/timeline.js +0 -139
- package/dist/commands/uninstall.js +0 -230
- package/dist/components/App.js +0 -109
- package/dist/components/Banner.js +0 -12
- package/dist/components/ConfirmPrompt.js +0 -25
- package/dist/components/DriftSummary.js +0 -23
- package/dist/components/GradeBadge.js +0 -15
- package/dist/components/HealthCard.js +0 -18
- package/dist/components/InkSpinner.js +0 -22
- package/dist/components/InputBox.js +0 -17
- package/dist/components/KeyValue.js +0 -13
- package/dist/components/MessageList.js +0 -14
- package/dist/components/ProgressBar.js +0 -26
- package/dist/components/Section.js +0 -16
- package/dist/components/SessionSummaryCard.js +0 -73
- package/dist/components/StartupDisplay.js +0 -24
- package/dist/components/StatusDashboard.js +0 -57
- package/dist/components/StatusLine.js +0 -8
- package/dist/components/StepLine.js +0 -22
- package/dist/components/Theme.js +0 -20
- package/dist/components/ToolProgress.js +0 -8
- package/dist/components/ViolationList.js +0 -21
- package/dist/components/render.js +0 -13
- package/dist/config/agent-registry.js +0 -237
- package/dist/config/claude-settings-hooks.js +0 -304
- package/dist/config/hook-installer.js +0 -65
- package/dist/config/instruction-writer.js +0 -388
- package/dist/config/mcp-config-writer.js +0 -266
- package/dist/config/settings.js +0 -174
- package/dist/config/tool-detector.js +0 -42
- package/dist/config/value-surfacing.js +0 -119
- package/dist/core/context-assembly.js +0 -108
- package/dist/core/conversation.js +0 -33
- package/dist/core/local-chat-provider.js +0 -475
- package/dist/core/provider-factory.js +0 -55
- package/dist/core/providers.js +0 -90
- package/dist/core/query-engine.js +0 -174
- package/dist/daemon/api.js +0 -312
- package/dist/daemon/autostart.js +0 -119
- package/dist/daemon/bootstrap.js +0 -39
- package/dist/daemon/client.js +0 -164
- package/dist/daemon/detect-ci.js +0 -81
- package/dist/daemon/platform-linux.js +0 -146
- package/dist/daemon/platform-macos.js +0 -134
- package/dist/daemon/platform-windows.js +0 -116
- package/dist/daemon/process-manager.js +0 -299
- package/dist/daemon/protocol.js +0 -23
- package/dist/daemon/registry.js +0 -270
- package/dist/daemon/settings-schema.js +0 -72
- package/dist/daemon/system-health.js +0 -134
- package/dist/daemon/version-checker.js +0 -262
- package/dist/daemon/warm-start.js +0 -223
- package/dist/entrypoints/cli.js +0 -1043
- package/dist/entrypoints/daemon.js +0 -380
- package/dist/entrypoints/repl.js +0 -147
- package/dist/hooks/adapters/claude-code.js +0 -90
- package/dist/hooks/adapters/cline.js +0 -100
- package/dist/hooks/adapters/cursor.js +0 -98
- package/dist/hooks/hook-dedup.js +0 -79
- package/dist/hooks/hook-runner.js +0 -113
- package/dist/hooks/navigation-hooks.js +0 -175
- package/dist/hooks/prompt-hooks.js +0 -63
- package/dist/hooks/shell-hooks.js +0 -47
- package/dist/ignore.js +0 -111
- package/dist/intelligence/approach-suggester.js +0 -61
- package/dist/intelligence/ast-extractor.js +0 -2615
- package/dist/intelligence/ast-worker.js +0 -34
- package/dist/intelligence/background-indexer.js +0 -121
- package/dist/intelligence/blast-radius.js +0 -200
- package/dist/intelligence/community-detection.js +0 -691
- package/dist/intelligence/community-detector.js +0 -184
- package/dist/intelligence/computation-scheduler.js +0 -75
- package/dist/intelligence/confidence-propagation.js +0 -47
- package/dist/intelligence/convention-detector.js +0 -242
- package/dist/intelligence/convention-learner.js +0 -205
- package/dist/intelligence/convention-matcher.js +0 -205
- package/dist/intelligence/cozo-schema.js +0 -376
- package/dist/intelligence/decision-point-detector.js +0 -90
- package/dist/intelligence/deep-dive-tools.js +0 -586
- package/dist/intelligence/durability-scorer.js +0 -84
- package/dist/intelligence/exploration-cost.js +0 -204
- package/dist/intelligence/exploration-pattern-tracker.js +0 -61
- package/dist/intelligence/fact-generator.js +0 -322
- package/dist/intelligence/facts-schema.js +0 -90
- package/dist/intelligence/file-intelligence.js +0 -59
- package/dist/intelligence/graph-holder.js +0 -220
- package/dist/intelligence/graph-temporal-joiner.js +0 -238
- package/dist/intelligence/health-grade.js +0 -423
- package/dist/intelligence/health-grader.js +0 -200
- package/dist/intelligence/health-map-data.js +0 -259
- package/dist/intelligence/import-symbols.js +0 -136
- package/dist/intelligence/incremental-indexer.js +0 -658
- package/dist/intelligence/indexer/centrality.js +0 -62
- package/dist/intelligence/indexer/cfg-context.js +0 -95
- package/dist/intelligence/indexer/confidence.js +0 -34
- package/dist/intelligence/indexer/cross-file-resolver.js +0 -104
- package/dist/intelligence/indexer/edge-repair.js +0 -89
- package/dist/intelligence/indexer/entity-key.js +0 -17
- package/dist/intelligence/indexer/export-map.js +0 -132
- package/dist/intelligence/indexer/git-cochange.js +0 -128
- package/dist/intelligence/indexer/graph-patch.js +0 -147
- package/dist/intelligence/indexer/incremental.js +0 -78
- package/dist/intelligence/indexer/ingest.js +0 -160
- package/dist/intelligence/indexer/language-detect.js +0 -226
- package/dist/intelligence/indexer/metadata.js +0 -63
- package/dist/intelligence/indexer/mutation-tracker.js +0 -79
- package/dist/intelligence/indexer/orchestrator.js +0 -155
- package/dist/intelligence/indexer/plugin-interface.js +0 -31
- package/dist/intelligence/indexer/plugins/csharp.js +0 -440
- package/dist/intelligence/indexer/plugins/go.js +0 -335
- package/dist/intelligence/indexer/plugins/java.js +0 -370
- package/dist/intelligence/indexer/plugins/python.js +0 -358
- package/dist/intelligence/indexer/plugins/regex-fallback.js +0 -82
- package/dist/intelligence/indexer/plugins/ruby.js +0 -290
- package/dist/intelligence/indexer/plugins/rust.js +0 -484
- package/dist/intelligence/indexer/plugins/tier2-generic.js +0 -310
- package/dist/intelligence/indexer/plugins/typescript.js +0 -456
- package/dist/intelligence/indexer/resource-monitor.js +0 -93
- package/dist/intelligence/indexer/scip/decoder.js +0 -253
- package/dist/intelligence/indexer/scip/detector.js +0 -232
- package/dist/intelligence/indexer/scip/downloader.js +0 -427
- package/dist/intelligence/indexer/scip/fallback.js +0 -34
- package/dist/intelligence/indexer/scip/merger.js +0 -109
- package/dist/intelligence/indexer/scip/orchestrator.js +0 -433
- package/dist/intelligence/indexer/scip/runner.js +0 -98
- package/dist/intelligence/indexer/snapshot.js +0 -66
- package/dist/intelligence/indexer/test-detector.js +0 -196
- package/dist/intelligence/indexer/watch-integration.js +0 -61
- package/dist/intelligence/indexer/worker.js +0 -85
- package/dist/intelligence/local-convention-detector.js +0 -437
- package/dist/intelligence/local-embeddings.js +0 -190
- package/dist/intelligence/local-graph.js +0 -1946
- package/dist/intelligence/local-indexer.js +0 -1575
- package/dist/intelligence/local-llm.js +0 -163
- package/dist/intelligence/local-rule-generator.js +0 -154
- package/dist/intelligence/local-snapshot.js +0 -213
- package/dist/intelligence/negative-knowledge.js +0 -103
- package/dist/intelligence/persistent-db.js +0 -85
- package/dist/intelligence/query-router.js +0 -2556
- package/dist/intelligence/risk-classifier.js +0 -116
- package/dist/intelligence/rule-evaluator.js +0 -380
- package/dist/intelligence/rule-generator.js +0 -49
- package/dist/intelligence/search-index.js +0 -173
- package/dist/intelligence/semantic/docstring-extractor.js +0 -67
- package/dist/intelligence/semantic/embedding-store.js +0 -52
- package/dist/intelligence/semantic/enrichment-orchestrator.js +0 -48
- package/dist/intelligence/semantic/git-message-miner.js +0 -114
- package/dist/intelligence/semantic/identifier-tokenizer.js +0 -51
- package/dist/intelligence/semantic/node2vec-embeddings.js +0 -71
- package/dist/intelligence/semantic/node2vec-walks.js +0 -103
- package/dist/intelligence/semantic/path-domain-inference.js +0 -112
- package/dist/intelligence/semantic/similarity-engine.js +0 -60
- package/dist/intelligence/semantic/tfidf-vectors.js +0 -88
- package/dist/intelligence/session-brief-builder.js +0 -159
- package/dist/intelligence/session-context.js +0 -221
- package/dist/intelligence/session-health-monitor.js +0 -211
- package/dist/intelligence/session-narrative.js +0 -197
- package/dist/intelligence/session-pattern-analyzer.js +0 -218
- package/dist/intelligence/signal-scorer.js +0 -390
- package/dist/intelligence/signal-show-store.js +0 -182
- package/dist/intelligence/smart-truncate.js +0 -158
- package/dist/intelligence/subgraph-cache.js +0 -88
- package/dist/intelligence/temporal-facts.js +0 -494
- package/dist/intelligence/token-estimator.js +0 -100
- package/dist/intelligence/tool-injector.js +0 -87
- package/dist/intelligence/tree-sitter-loader.js +0 -71
- package/dist/intelligence/worker-pool.js +0 -116
- package/dist/proxy/arg-validator.js +0 -79
- package/dist/proxy/auto-bootstrap.js +0 -167
- package/dist/proxy/bridge.js +0 -147
- package/dist/proxy/budget-enforcer.js +0 -70
- package/dist/proxy/compression-quality-monitor.js +0 -160
- package/dist/proxy/compression-stats.js +0 -51
- package/dist/proxy/context-rot-detector.js +0 -137
- package/dist/proxy/drift-detector.js +0 -139
- package/dist/proxy/efficiency-tracker.js +0 -79
- package/dist/proxy/fact-ranking.js +0 -154
- package/dist/proxy/format-encoder.js +0 -266
- package/dist/proxy/http-transport.js +0 -90
- package/dist/proxy/lifecycle-actor.js +0 -55
- package/dist/proxy/lifecycle-machine.js +0 -187
- package/dist/proxy/log-tailer.js +0 -265
- package/dist/proxy/model-pricing.js +0 -98
- package/dist/proxy/network-firewall.js +0 -141
- package/dist/proxy/nudge-state.js +0 -93
- package/dist/proxy/output-compressor.js +0 -185
- package/dist/proxy/pid-lock.js +0 -291
- package/dist/proxy/proxy-context.js +0 -11
- package/dist/proxy/proxy.js +0 -2633
- package/dist/proxy/response-enrichment.js +0 -32
- package/dist/proxy/response-envelope.js +0 -313
- package/dist/proxy/session-dedup.js +0 -82
- package/dist/proxy/session-legend.js +0 -30
- package/dist/proxy/session-persistence.js +0 -210
- package/dist/proxy/session-resume.js +0 -94
- package/dist/proxy/session-stats.js +0 -513
- package/dist/proxy/shell-classifier.js +0 -1346
- package/dist/proxy/shell-compression-log.js +0 -93
- package/dist/proxy/shell-compressor.js +0 -390
- package/dist/proxy/shell-graph-boost.js +0 -202
- package/dist/proxy/shell-monitor-map.js +0 -18
- package/dist/proxy/shell-stats.js +0 -54
- package/dist/proxy/shell-strategies/cloud.js +0 -215
- package/dist/proxy/shell-strategies/diff.js +0 -159
- package/dist/proxy/shell-strategies/error-diagnostic.js +0 -796
- package/dist/proxy/shell-strategies/filter-dsl.js +0 -358
- package/dist/proxy/shell-strategies/git-status.js +0 -177
- package/dist/proxy/shell-strategies/key-value.js +0 -193
- package/dist/proxy/shell-strategies/log-text.js +0 -154
- package/dist/proxy/shell-strategies/omni.js +0 -188
- package/dist/proxy/shell-strategies/progress.js +0 -55
- package/dist/proxy/shell-strategies/redact.js +0 -76
- package/dist/proxy/shell-strategies/structured.js +0 -241
- package/dist/proxy/shell-strategies/tabular.js +0 -243
- package/dist/proxy/shell-strategies/test-results-types.js +0 -13
- package/dist/proxy/shell-strategies/test-results.js +0 -784
- package/dist/proxy/shell-strategies/tree-paths.js +0 -144
- package/dist/proxy/shell-strategies/yaml.js +0 -182
- package/dist/proxy/shell-tee.js +0 -111
- package/dist/proxy/signal-dedup.js +0 -171
- package/dist/proxy/startup-renderer.js +0 -158
- package/dist/proxy/task-token-display.js +0 -38
- package/dist/proxy/token-counter.js +0 -61
- package/dist/proxy/tool-clusters.js +0 -273
- package/dist/proxy/tool-definitions.js +0 -525
- package/dist/proxy/transport-mux.js +0 -229
- package/dist/proxy/wire-cap.js +0 -268
- package/dist/rules/developer.mozilla.org.json +0 -9
- package/dist/rules/github.com.json +0 -21
- package/dist/schemas/api/skills.js +0 -19
- package/dist/schemas/common/errors.js +0 -7
- package/dist/schemas/common/headers.js +0 -5
- package/dist/schemas/entities/edge.js +0 -25
- package/dist/schemas/entities/entity.js +0 -22
- package/dist/schemas/entities/rule.js +0 -18
- package/dist/schemas/index.js +0 -14
- package/dist/server/event-bus.js +0 -59
- package/dist/server/http.js +0 -156
- package/dist/server/middleware.js +0 -70
- package/dist/server/routes/drift.js +0 -97
- package/dist/server/routes/intelligence.js +0 -1217
- package/dist/server/routes/reasoning-quality.js +0 -444
- package/dist/server/routes/session.js +0 -86
- package/dist/server/routes/stream.js +0 -120
- package/dist/server/routes/system.js +0 -73
- package/dist/server/routes/temporal.js +0 -170
- package/dist/server/routes/timeline.js +0 -232
- package/dist/server/routes/token-flow.js +0 -403
- package/dist/skills/effectiveness-tracker.js +0 -93
- package/dist/skills/local-pack.js +0 -380
- package/dist/skills/resolver.js +0 -495
- package/dist/state-detector.js +0 -83
- package/dist/timeline/intent-detector.js +0 -263
- package/dist/timeline/loop-miner.js +0 -140
- package/dist/timeline/open-threads.js +0 -49
- package/dist/timeline/signal-reinforcer.js +0 -62
- package/dist/timeline/timeline-bootstrap.js +0 -151
- package/dist/timeline/timeline-store.js +0 -618
- package/dist/tools/coding/bash.js +0 -49
- package/dist/tools/coding/file-edit.js +0 -72
- package/dist/tools/coding/file-outline.js +0 -227
- package/dist/tools/coding/file-read-protocol.js +0 -425
- package/dist/tools/coding/file-read.js +0 -35
- package/dist/tools/coding/file-write.js +0 -43
- package/dist/tools/coding/glob-tool.js +0 -109
- package/dist/tools/coding/grep.js +0 -162
- package/dist/tools/coding/index.js +0 -27
- package/dist/tools/intelligence/index.js +0 -269
- package/dist/tools/intelligence/record-fact.js +0 -48
- package/dist/tools/intelligence/timeline-markers.js +0 -130
- package/dist/tools/registry.js +0 -47
- package/dist/tools/types.js +0 -8
- package/dist/tracking/auto-snapshot-triggers.js +0 -246
- package/dist/tracking/branch-context.js +0 -115
- package/dist/tracking/branch-snapshot.js +0 -217
- package/dist/tracking/causal-bridge.js +0 -317
- package/dist/tracking/circuit-breaker.js +0 -147
- package/dist/tracking/commit-watcher.js +0 -114
- package/dist/tracking/context-ledger.js +0 -119
- package/dist/tracking/correction-detector.js +0 -324
- package/dist/tracking/drift-tracker.js +0 -874
- package/dist/tracking/durability-tracker.js +0 -94
- package/dist/tracking/entity-rewind.js +0 -200
- package/dist/tracking/file-hash-state.js +0 -114
- package/dist/tracking/git-attribution.js +0 -132
- package/dist/tracking/git-trailers.js +0 -171
- package/dist/tracking/intelligence-counter.js +0 -46
- package/dist/tracking/intent-correlator.js +0 -202
- package/dist/tracking/intent-encoder.js +0 -52
- package/dist/tracking/intent-token-tracker.js +0 -159
- package/dist/tracking/ledger-archiver.js +0 -94
- package/dist/tracking/ledger-chains.js +0 -245
- package/dist/tracking/metrics-store.js +0 -361
- package/dist/tracking/native-watcher.js +0 -131
- package/dist/tracking/offline-rewind.js +0 -295
- package/dist/tracking/pending-violations.js +0 -74
- package/dist/tracking/persistence-effectiveness.js +0 -167
- package/dist/tracking/prompt-durability.js +0 -202
- package/dist/tracking/quality-signals.js +0 -213
- package/dist/tracking/redactor.js +0 -73
- package/dist/tracking/rewind-engine.js +0 -161
- package/dist/tracking/session-history.js +0 -128
- package/dist/tracking/session-receipt.js +0 -88
- package/dist/tracking/session-summary-writer.js +0 -157
- package/dist/tracking/shadow-ledger.js +0 -321
- package/dist/tracking/stash-manager.js +0 -258
- package/dist/tracking/timeline-fork.js +0 -213
- package/dist/tracking/timeline.js +0 -69
- package/dist/tracking/token-flow.js +0 -276
- package/dist/tracking/turn-segmenter.js +0 -122
- package/dist/tracking/weekly-accumulator.js +0 -179
- package/dist/tracking/working-snapshots.js +0 -188
- package/dist/tracking/workspace-manifest.js +0 -176
- package/dist/transport/http.js +0 -102
- package/dist/utils/counterfactual.js +0 -65
- package/dist/utils/deep-link.js +0 -34
- package/dist/utils/detect.js +0 -193
- package/dist/utils/exec.js +0 -73
- package/dist/utils/file-logger.js +0 -87
- package/dist/utils/format-error.js +0 -29
- package/dist/utils/git.js +0 -181
- package/dist/utils/log.js +0 -57
- package/dist/utils/logger.js +0 -35
- package/dist/utils/mcp-content-json.js +0 -8
- package/dist/utils/session-logger.js +0 -154
- package/dist/utils/startup-log.js +0 -512
- package/dist/utils/ui.js +0 -56
|
@@ -1,174 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Query Engine — the LLM tool-call loop.
|
|
3
|
-
*
|
|
4
|
-
* Orchestrates: system prompt assembly → LLM streaming → tool execution → loop.
|
|
5
|
-
* Decoupled from UI — emits events that any consumer (REPL, SDK, headless) can subscribe to.
|
|
6
|
-
*
|
|
7
|
-
* Supports both Anthropic SDK and BYO-LLM (local) via the ChatProvider interface.
|
|
8
|
-
*/
|
|
9
|
-
import { AiSdkChatProvider, AnthropicChatProvider, toChatToolDefs, } from "./local-chat-provider.js";
|
|
10
|
-
// ── Cost Table (per million tokens) ───────────────────────────
|
|
11
|
-
const COST_TABLE = {
|
|
12
|
-
"claude-sonnet-4-20250514": { input: 3, output: 15 },
|
|
13
|
-
"claude-opus-4-20250514": { input: 15, output: 75 },
|
|
14
|
-
"claude-haiku-4-20250506": { input: 0.8, output: 4 },
|
|
15
|
-
};
|
|
16
|
-
function estimateCost(model, inputTokens, outputTokens) {
|
|
17
|
-
const rates = COST_TABLE[model] ?? { input: 3, output: 15 };
|
|
18
|
-
return (inputTokens * rates.input + outputTokens * rates.output) / 1_000_000;
|
|
19
|
-
}
|
|
20
|
-
// ── Conversion Helpers ────────────────────────────────────────
|
|
21
|
-
/**
|
|
22
|
-
* Convert our conversation messages to ChatMessage format.
|
|
23
|
-
* Handles tool_use/tool_result pairs.
|
|
24
|
-
*/
|
|
25
|
-
function toChatMessages(messages) {
|
|
26
|
-
const result = [];
|
|
27
|
-
for (const msg of messages) {
|
|
28
|
-
if (msg.role === "user") {
|
|
29
|
-
result.push({ role: "user", content: msg.content });
|
|
30
|
-
}
|
|
31
|
-
else {
|
|
32
|
-
if (msg.toolCalls && msg.toolCalls.length > 0) {
|
|
33
|
-
// Assistant message with tool calls
|
|
34
|
-
const toolCallsFormatted = msg.toolCalls.map((tc) => ({
|
|
35
|
-
id: `toolu_${Date.now()}_${tc.name}`,
|
|
36
|
-
type: "function",
|
|
37
|
-
function: {
|
|
38
|
-
name: tc.name,
|
|
39
|
-
arguments: JSON.stringify(tc.args),
|
|
40
|
-
},
|
|
41
|
-
}));
|
|
42
|
-
result.push({
|
|
43
|
-
role: "assistant",
|
|
44
|
-
content: msg.content || "",
|
|
45
|
-
tool_calls: toolCallsFormatted,
|
|
46
|
-
});
|
|
47
|
-
// Add tool result messages
|
|
48
|
-
for (let i = 0; i < msg.toolCalls.length; i++) {
|
|
49
|
-
const tc = msg.toolCalls[i];
|
|
50
|
-
if (!tc)
|
|
51
|
-
continue;
|
|
52
|
-
result.push({
|
|
53
|
-
role: "tool",
|
|
54
|
-
content: typeof tc.result.content === "string"
|
|
55
|
-
? tc.result.content
|
|
56
|
-
: JSON.stringify(tc.result.content),
|
|
57
|
-
tool_call_id: toolCallsFormatted[i]?.id,
|
|
58
|
-
});
|
|
59
|
-
}
|
|
60
|
-
}
|
|
61
|
-
else {
|
|
62
|
-
result.push({ role: "assistant", content: msg.content });
|
|
63
|
-
}
|
|
64
|
-
}
|
|
65
|
-
}
|
|
66
|
-
return result;
|
|
67
|
-
}
|
|
68
|
-
// ── Engine ────────────────────────────────────────────────────
|
|
69
|
-
/**
|
|
70
|
-
* Resolve the ChatProvider from options.
|
|
71
|
-
* Priority: chatProvider > languageModel > apiKey (Anthropic fallback).
|
|
72
|
-
*/
|
|
73
|
-
function resolveProvider(options) {
|
|
74
|
-
if (options.chatProvider) {
|
|
75
|
-
return options.chatProvider;
|
|
76
|
-
}
|
|
77
|
-
if (options.languageModel) {
|
|
78
|
-
return new AiSdkChatProvider(options.languageModel, options.providerName ?? "ai-sdk", options.model);
|
|
79
|
-
}
|
|
80
|
-
if (!options.apiKey) {
|
|
81
|
-
throw new Error("QueryEngine requires one of: chatProvider, languageModel, or apiKey");
|
|
82
|
-
}
|
|
83
|
-
return new AnthropicChatProvider(options.apiKey, options.model);
|
|
84
|
-
}
|
|
85
|
-
/**
|
|
86
|
-
* Execute a single query turn: send messages to the LLM, handle tool calls, return result.
|
|
87
|
-
*
|
|
88
|
-
* The loop:
|
|
89
|
-
* 1. Send conversation + system prompt + tool defs to LLM (streaming)
|
|
90
|
-
* 2. Accumulate text tokens, emit onToken events
|
|
91
|
-
* 3. When the LLM requests tool calls, execute each tool
|
|
92
|
-
* 4. Feed tool results back as a new turn, loop to step 1
|
|
93
|
-
* 5. When the LLM produces no more tool calls, return the full result
|
|
94
|
-
*/
|
|
95
|
-
export async function executeQuery(messages, options, events) {
|
|
96
|
-
const provider = resolveProvider(options);
|
|
97
|
-
const chatToolDefs = toChatToolDefs(options.tools);
|
|
98
|
-
const toolMap = new Map(options.tools.map((t) => [t.name, t]));
|
|
99
|
-
let totalInputTokens = 0;
|
|
100
|
-
let totalOutputTokens = 0;
|
|
101
|
-
const allToolCalls = [];
|
|
102
|
-
const workingMessages = toChatMessages(messages);
|
|
103
|
-
let finalResponse = "";
|
|
104
|
-
// Tool-call loop — keep going until the LLM produces no tool calls
|
|
105
|
-
while (true) {
|
|
106
|
-
const response = await provider.streamChat(workingMessages, chatToolDefs, options.systemPrompt, options.maxTokens ?? 8192, (chunk) => {
|
|
107
|
-
if (chunk.type === "text_delta" && chunk.text) {
|
|
108
|
-
events?.onToken?.(chunk.text);
|
|
109
|
-
}
|
|
110
|
-
});
|
|
111
|
-
totalInputTokens += response.inputTokens;
|
|
112
|
-
totalOutputTokens += response.outputTokens;
|
|
113
|
-
// If no tool calls, we're done
|
|
114
|
-
if (response.toolCalls.length === 0) {
|
|
115
|
-
finalResponse = response.text;
|
|
116
|
-
break;
|
|
117
|
-
}
|
|
118
|
-
// Execute tool calls
|
|
119
|
-
const toolCallMessages = [];
|
|
120
|
-
// Add assistant message with tool calls
|
|
121
|
-
workingMessages.push({
|
|
122
|
-
role: "assistant",
|
|
123
|
-
content: response.text || "",
|
|
124
|
-
tool_calls: response.toolCalls.map((tc) => ({
|
|
125
|
-
id: tc.id,
|
|
126
|
-
type: "function",
|
|
127
|
-
function: {
|
|
128
|
-
name: tc.name,
|
|
129
|
-
arguments: JSON.stringify(tc.args),
|
|
130
|
-
},
|
|
131
|
-
})),
|
|
132
|
-
});
|
|
133
|
-
for (const toolCall of response.toolCalls) {
|
|
134
|
-
const tool = toolMap.get(toolCall.name);
|
|
135
|
-
events?.onToolCall?.(toolCall.name, toolCall.args);
|
|
136
|
-
let result;
|
|
137
|
-
if (!tool) {
|
|
138
|
-
result = { content: `Unknown tool: ${toolCall.name}`, isError: true };
|
|
139
|
-
}
|
|
140
|
-
else {
|
|
141
|
-
try {
|
|
142
|
-
result = await tool.execute(toolCall.args, options.toolContext);
|
|
143
|
-
}
|
|
144
|
-
catch (err) {
|
|
145
|
-
result = {
|
|
146
|
-
content: `Tool error: ${err instanceof Error ? err.message : String(err)}`,
|
|
147
|
-
isError: true,
|
|
148
|
-
};
|
|
149
|
-
}
|
|
150
|
-
}
|
|
151
|
-
events?.onToolResult?.(toolCall.name, result);
|
|
152
|
-
allToolCalls.push({ name: toolCall.name, args: toolCall.args, result });
|
|
153
|
-
const contentStr = typeof result.content === "string"
|
|
154
|
-
? result.content
|
|
155
|
-
: JSON.stringify(result.content);
|
|
156
|
-
workingMessages.push({
|
|
157
|
-
role: "tool",
|
|
158
|
-
content: contentStr,
|
|
159
|
-
tool_call_id: toolCall.id,
|
|
160
|
-
});
|
|
161
|
-
}
|
|
162
|
-
}
|
|
163
|
-
const queryResult = {
|
|
164
|
-
response: finalResponse,
|
|
165
|
-
toolCalls: allToolCalls,
|
|
166
|
-
usage: {
|
|
167
|
-
inputTokens: totalInputTokens,
|
|
168
|
-
outputTokens: totalOutputTokens,
|
|
169
|
-
estimatedCost: estimateCost(provider.modelId, totalInputTokens, totalOutputTokens),
|
|
170
|
-
},
|
|
171
|
-
};
|
|
172
|
-
events?.onDone?.(queryResult);
|
|
173
|
-
return queryResult;
|
|
174
|
-
}
|
package/dist/daemon/api.js
DELETED
|
@@ -1,312 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* unerrd HTTP API — served on port 9847 by the daemon supervisor.
|
|
3
|
-
*
|
|
4
|
-
* Route layout:
|
|
5
|
-
* /api/daemon — supervisor metadata (uptime, version, pid)
|
|
6
|
-
* /api/repos — all registered repos with status
|
|
7
|
-
* /api/repos/aggregate — aggregated metrics across all running repos
|
|
8
|
-
* /api/repo/:label/* — proxy to the per-repo HTTP API (token-flow, reasoning, etc.)
|
|
9
|
-
*
|
|
10
|
-
* Static assets (the React SPA) are served from the same dist/ui/ directory
|
|
11
|
-
* used by per-repo dashboards. The SPA detects whether it's served by unerrd
|
|
12
|
-
* (via /api/daemon) and shows the global overview vs per-repo view.
|
|
13
|
-
*/
|
|
14
|
-
import { existsSync, readFileSync } from "node:fs";
|
|
15
|
-
import { request as httpRequest } from "node:http";
|
|
16
|
-
import { dirname, join } from "node:path";
|
|
17
|
-
import { fileURLToPath } from "node:url";
|
|
18
|
-
import { serve } from "@hono/node-server";
|
|
19
|
-
import { serveStatic } from "@hono/node-server/serve-static";
|
|
20
|
-
import { Hono } from "hono";
|
|
21
|
-
import { cors } from "hono/cors";
|
|
22
|
-
import { listRepos } from "./registry.js";
|
|
23
|
-
const DAEMON_PORT = 9847;
|
|
24
|
-
export function startDaemonApi(pm) {
|
|
25
|
-
const app = new Hono();
|
|
26
|
-
const startedAt = Date.now();
|
|
27
|
-
app.use("*", cors({ origin: "*" }));
|
|
28
|
-
// ── /api/daemon — supervisor metadata ───────────────────────
|
|
29
|
-
app.get("/api/daemon", (c) => {
|
|
30
|
-
return c.json({
|
|
31
|
-
pid: process.pid,
|
|
32
|
-
uptime: Math.round((Date.now() - startedAt) / 1000),
|
|
33
|
-
startedAt: new Date(startedAt).toISOString(),
|
|
34
|
-
version: "0.1.0",
|
|
35
|
-
port: DAEMON_PORT,
|
|
36
|
-
});
|
|
37
|
-
});
|
|
38
|
-
// ── /api/repos — all repos with live status ─────────────────
|
|
39
|
-
app.get("/api/repos", (c) => {
|
|
40
|
-
const repos = pm.getStatus();
|
|
41
|
-
return c.json({ repos });
|
|
42
|
-
});
|
|
43
|
-
// ── /api/repos/aggregate — cross-repo metrics ───────────────
|
|
44
|
-
app.get("/api/repos/aggregate", async (c) => {
|
|
45
|
-
const repos = pm.getStatus();
|
|
46
|
-
const running = repos.filter((r) => r.status === "running");
|
|
47
|
-
let totalEntities = 0;
|
|
48
|
-
let totalEdges = 0;
|
|
49
|
-
let totalMemory = 0;
|
|
50
|
-
let totalConnections = 0;
|
|
51
|
-
let totalNeedsInput = 0;
|
|
52
|
-
// Aggregate token-flow and reasoning data from running repos
|
|
53
|
-
const tokenFlowAgg = { saved: 0, total: 0, violations: 0 };
|
|
54
|
-
const reasoningAgg = { sessions: 0, avgQuality: 0, totalQualities: 0 };
|
|
55
|
-
for (const repo of repos) {
|
|
56
|
-
totalEntities += repo.entityCount ?? 0;
|
|
57
|
-
totalEdges += repo.edgeCount ?? 0;
|
|
58
|
-
totalMemory += repo.memory ?? 0;
|
|
59
|
-
totalConnections += repo.connections;
|
|
60
|
-
totalNeedsInput += repo.needsInput.length;
|
|
61
|
-
}
|
|
62
|
-
// Fetch per-repo stats from running processes' HTTP APIs
|
|
63
|
-
const perRepoStats = await Promise.allSettled(running.map(async (repo) => {
|
|
64
|
-
if (!repo.pid)
|
|
65
|
-
return null;
|
|
66
|
-
const managed = pm.getManaged(repo.path);
|
|
67
|
-
if (!managed?.sock)
|
|
68
|
-
return null;
|
|
69
|
-
// Try to read server.json from the repo's .unerr/state/ to get its HTTP port
|
|
70
|
-
const serverJsonPath = join(repo.path, ".unerr", "state", "server.json");
|
|
71
|
-
if (!existsSync(serverJsonPath))
|
|
72
|
-
return null;
|
|
73
|
-
try {
|
|
74
|
-
const serverInfo = JSON.parse(readFileSync(serverJsonPath, "utf-8"));
|
|
75
|
-
const [sessionStats, tokenFlow] = await Promise.allSettled([
|
|
76
|
-
fetchRepoApi(serverInfo.port, "/api/session/stats"),
|
|
77
|
-
fetchRepoApi(serverInfo.port, "/api/token-flow/summary"),
|
|
78
|
-
]);
|
|
79
|
-
return {
|
|
80
|
-
label: repo.label,
|
|
81
|
-
port: serverInfo.port,
|
|
82
|
-
sessionStats: sessionStats.status === "fulfilled" ? sessionStats.value : null,
|
|
83
|
-
tokenFlow: tokenFlow.status === "fulfilled" ? tokenFlow.value : null,
|
|
84
|
-
};
|
|
85
|
-
}
|
|
86
|
-
catch {
|
|
87
|
-
return null;
|
|
88
|
-
}
|
|
89
|
-
}));
|
|
90
|
-
for (const result of perRepoStats) {
|
|
91
|
-
if (result.status !== "fulfilled" || !result.value)
|
|
92
|
-
continue;
|
|
93
|
-
const stats = result.value;
|
|
94
|
-
if (stats.sessionStats) {
|
|
95
|
-
const s = stats.sessionStats;
|
|
96
|
-
tokenFlowAgg.saved += s.data?.estimatedTokensSaved ?? 0;
|
|
97
|
-
tokenFlowAgg.total += s.data?.toolCallsLocal ?? 0;
|
|
98
|
-
tokenFlowAgg.violations += s.data?.violationsCaught ?? 0;
|
|
99
|
-
}
|
|
100
|
-
}
|
|
101
|
-
return c.json({
|
|
102
|
-
summary: {
|
|
103
|
-
totalRepos: repos.length,
|
|
104
|
-
runningRepos: running.length,
|
|
105
|
-
stoppedRepos: repos.length - running.length,
|
|
106
|
-
totalEntities,
|
|
107
|
-
totalEdges,
|
|
108
|
-
totalMemoryMb: totalMemory,
|
|
109
|
-
totalConnections,
|
|
110
|
-
totalNeedsInput,
|
|
111
|
-
},
|
|
112
|
-
tokenFlow: tokenFlowAgg,
|
|
113
|
-
reasoning: reasoningAgg,
|
|
114
|
-
});
|
|
115
|
-
});
|
|
116
|
-
// ── /api/daemon/warm-start — warm-start status ───────────────
|
|
117
|
-
const warmStartEvents = [];
|
|
118
|
-
let warmStartLastRun = null;
|
|
119
|
-
app.get("/api/daemon/warm-start", async (c) => {
|
|
120
|
-
let config = {
|
|
121
|
-
warmStartBudget: 3,
|
|
122
|
-
warmStartIdleDays: 14,
|
|
123
|
-
warmStartDelayMs: 30_000,
|
|
124
|
-
};
|
|
125
|
-
try {
|
|
126
|
-
const mod = await import("./warm-start.js");
|
|
127
|
-
config = mod.loadWarmStartConfig();
|
|
128
|
-
}
|
|
129
|
-
catch {
|
|
130
|
-
// Module not available
|
|
131
|
-
}
|
|
132
|
-
return c.json({
|
|
133
|
-
lastRun: warmStartLastRun,
|
|
134
|
-
events: warmStartEvents,
|
|
135
|
-
config,
|
|
136
|
-
});
|
|
137
|
-
});
|
|
138
|
-
// Public method for daemon.ts to push warm-start events
|
|
139
|
-
const pushWarmStartEvent = (event) => {
|
|
140
|
-
warmStartEvents.push(event);
|
|
141
|
-
warmStartLastRun = new Date().toISOString();
|
|
142
|
-
};
|
|
143
|
-
// ── /api/daemon/version — update notification ────────────────
|
|
144
|
-
app.get("/api/daemon/version", async (c) => {
|
|
145
|
-
try {
|
|
146
|
-
const { getCachedUpdateInfo } = await import("./version-checker.js");
|
|
147
|
-
return c.json(getCachedUpdateInfo());
|
|
148
|
-
}
|
|
149
|
-
catch {
|
|
150
|
-
return c.json({
|
|
151
|
-
available: false,
|
|
152
|
-
current: "0.0.1",
|
|
153
|
-
latest: "0.0.1",
|
|
154
|
-
behindMinor: 0,
|
|
155
|
-
dismissed: false,
|
|
156
|
-
});
|
|
157
|
-
}
|
|
158
|
-
});
|
|
159
|
-
app.post("/api/daemon/version/dismiss", async (c) => {
|
|
160
|
-
try {
|
|
161
|
-
const body = await c.req.json();
|
|
162
|
-
if (body.version) {
|
|
163
|
-
const { dismissVersion } = await import("./version-checker.js");
|
|
164
|
-
dismissVersion(body.version);
|
|
165
|
-
}
|
|
166
|
-
return c.json({ ok: true });
|
|
167
|
-
}
|
|
168
|
-
catch {
|
|
169
|
-
return c.json({ ok: false }, 400);
|
|
170
|
-
}
|
|
171
|
-
});
|
|
172
|
-
// ── /api/repo/:label/* — proxy to per-repo HTTP ─────────────
|
|
173
|
-
app.all("/api/repo/:label/*", async (c) => {
|
|
174
|
-
const label = c.req.param("label");
|
|
175
|
-
const repos = listRepos();
|
|
176
|
-
const repo = repos.find((r) => r.label === label);
|
|
177
|
-
if (!repo) {
|
|
178
|
-
return c.json({ error: `Unknown repo: ${label}` }, 404);
|
|
179
|
-
}
|
|
180
|
-
const managed = pm.getManaged(repo.path);
|
|
181
|
-
if (!managed || managed.status !== "running") {
|
|
182
|
-
return c.json({
|
|
183
|
-
error: `Repo ${label} is not running`,
|
|
184
|
-
status: managed?.status ?? "stopped",
|
|
185
|
-
}, 503);
|
|
186
|
-
}
|
|
187
|
-
// Read the repo's server.json for its HTTP port
|
|
188
|
-
const serverJsonPath = join(repo.path, ".unerr", "state", "server.json");
|
|
189
|
-
if (!existsSync(serverJsonPath)) {
|
|
190
|
-
return c.json({ error: `Repo ${label} has no dashboard server` }, 503);
|
|
191
|
-
}
|
|
192
|
-
let repoPort;
|
|
193
|
-
try {
|
|
194
|
-
const info = JSON.parse(readFileSync(serverJsonPath, "utf-8"));
|
|
195
|
-
repoPort = info.port;
|
|
196
|
-
}
|
|
197
|
-
catch {
|
|
198
|
-
return c.json({ error: `Cannot read server.json for ${label}` }, 500);
|
|
199
|
-
}
|
|
200
|
-
// Strip /api/repo/:label prefix and prepend /api so that
|
|
201
|
-
// /api/repo/label/stats → /api/stats on the per-repo server.
|
|
202
|
-
const remaining = c.req.path.replace(`/api/repo/${label}`, "");
|
|
203
|
-
const targetPath = `/api${remaining || "/"}`;
|
|
204
|
-
try {
|
|
205
|
-
const proxyResponse = await proxyToRepoHttp(repoPort, targetPath, c.req.method);
|
|
206
|
-
return c.json(proxyResponse);
|
|
207
|
-
}
|
|
208
|
-
catch (err) {
|
|
209
|
-
return c.json({ error: `Proxy error: ${err.message}` }, 502);
|
|
210
|
-
}
|
|
211
|
-
});
|
|
212
|
-
// ── Static SPA serving ──────────────────────────────────────
|
|
213
|
-
const distDir = join(dirname(fileURLToPath(import.meta.url)), "ui");
|
|
214
|
-
const spaIndex = join(distDir, "index.html");
|
|
215
|
-
if (existsSync(spaIndex)) {
|
|
216
|
-
const spaHtml = readFileSync(spaIndex, "utf-8");
|
|
217
|
-
app.use("*", serveStatic({ root: distDir }));
|
|
218
|
-
app.get("*", (c) => {
|
|
219
|
-
if (c.req.path.startsWith("/api/"))
|
|
220
|
-
return c.notFound();
|
|
221
|
-
return c.html(spaHtml);
|
|
222
|
-
});
|
|
223
|
-
}
|
|
224
|
-
else {
|
|
225
|
-
app.get("/", (c) => {
|
|
226
|
-
return c.json({
|
|
227
|
-
message: "unerrd dashboard API is running. UI not built yet.",
|
|
228
|
-
hint: "Run 'pnpm run build' to build the dashboard SPA.",
|
|
229
|
-
api: {
|
|
230
|
-
daemon: "/api/daemon",
|
|
231
|
-
repos: "/api/repos",
|
|
232
|
-
aggregate: "/api/repos/aggregate",
|
|
233
|
-
proxy: "/api/repo/<label>/api/...",
|
|
234
|
-
},
|
|
235
|
-
});
|
|
236
|
-
});
|
|
237
|
-
}
|
|
238
|
-
// ── Start server ────────────────────────────────────────────
|
|
239
|
-
let server;
|
|
240
|
-
try {
|
|
241
|
-
server = serve({
|
|
242
|
-
fetch: app.fetch,
|
|
243
|
-
port: DAEMON_PORT,
|
|
244
|
-
hostname: "127.0.0.1",
|
|
245
|
-
});
|
|
246
|
-
}
|
|
247
|
-
catch {
|
|
248
|
-
process.stderr.write(`[unerrd] Port ${DAEMON_PORT} occupied, skipping dashboard.\n`);
|
|
249
|
-
return null;
|
|
250
|
-
}
|
|
251
|
-
return {
|
|
252
|
-
port: DAEMON_PORT,
|
|
253
|
-
close: () => {
|
|
254
|
-
try {
|
|
255
|
-
server.close();
|
|
256
|
-
}
|
|
257
|
-
catch {
|
|
258
|
-
/* already closed */
|
|
259
|
-
}
|
|
260
|
-
},
|
|
261
|
-
pushWarmStartEvent,
|
|
262
|
-
};
|
|
263
|
-
}
|
|
264
|
-
// ── Helpers ───────────────────────────────────────────────────────
|
|
265
|
-
function fetchRepoApi(port, path) {
|
|
266
|
-
return new Promise((resolve, reject) => {
|
|
267
|
-
const req = httpRequest({ hostname: "127.0.0.1", port, path, method: "GET", timeout: 3000 }, (res) => {
|
|
268
|
-
let body = "";
|
|
269
|
-
res.on("data", (chunk) => {
|
|
270
|
-
body += chunk.toString();
|
|
271
|
-
});
|
|
272
|
-
res.on("end", () => {
|
|
273
|
-
try {
|
|
274
|
-
resolve(JSON.parse(body));
|
|
275
|
-
}
|
|
276
|
-
catch {
|
|
277
|
-
resolve(body);
|
|
278
|
-
}
|
|
279
|
-
});
|
|
280
|
-
});
|
|
281
|
-
req.on("error", reject);
|
|
282
|
-
req.on("timeout", () => {
|
|
283
|
-
req.destroy();
|
|
284
|
-
reject(new Error("timeout"));
|
|
285
|
-
});
|
|
286
|
-
req.end();
|
|
287
|
-
});
|
|
288
|
-
}
|
|
289
|
-
function proxyToRepoHttp(port, path, method) {
|
|
290
|
-
return new Promise((resolve, reject) => {
|
|
291
|
-
const req = httpRequest({ hostname: "127.0.0.1", port, path, method, timeout: 10_000 }, (res) => {
|
|
292
|
-
let body = "";
|
|
293
|
-
res.on("data", (chunk) => {
|
|
294
|
-
body += chunk.toString();
|
|
295
|
-
});
|
|
296
|
-
res.on("end", () => {
|
|
297
|
-
try {
|
|
298
|
-
resolve(JSON.parse(body));
|
|
299
|
-
}
|
|
300
|
-
catch {
|
|
301
|
-
resolve(body);
|
|
302
|
-
}
|
|
303
|
-
});
|
|
304
|
-
});
|
|
305
|
-
req.on("error", reject);
|
|
306
|
-
req.on("timeout", () => {
|
|
307
|
-
req.destroy();
|
|
308
|
-
reject(new Error("Proxy timeout"));
|
|
309
|
-
});
|
|
310
|
-
req.end();
|
|
311
|
-
});
|
|
312
|
-
}
|
package/dist/daemon/autostart.js
DELETED
|
@@ -1,119 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Unified auto-start installer — dispatches to the correct platform module.
|
|
3
|
-
*
|
|
4
|
-
* Gated by:
|
|
5
|
-
* - ~/.unerr/.autostart-installed sentinel (never re-runs)
|
|
6
|
-
* - isCI() check (never installs in CI/containers)
|
|
7
|
-
*
|
|
8
|
-
* Called automatically on first `unerr install <agent>` or `unerr daemon add`.
|
|
9
|
-
*/
|
|
10
|
-
import { existsSync, mkdirSync, unlinkSync, writeFileSync } from "node:fs";
|
|
11
|
-
import { homedir } from "node:os";
|
|
12
|
-
import { join } from "node:path";
|
|
13
|
-
import { isCI } from "./detect-ci.js";
|
|
14
|
-
const SENTINEL_PATH = join(homedir(), ".unerr", ".autostart-installed");
|
|
15
|
-
export function isAutostartInstalled() {
|
|
16
|
-
return existsSync(SENTINEL_PATH);
|
|
17
|
-
}
|
|
18
|
-
/**
|
|
19
|
-
* Install platform auto-start if not already done.
|
|
20
|
-
* Returns null if skipped (already installed, CI, or unsupported platform).
|
|
21
|
-
*/
|
|
22
|
-
export async function autoInstallIfNeeded() {
|
|
23
|
-
if (isAutostartInstalled())
|
|
24
|
-
return null;
|
|
25
|
-
if (isCI())
|
|
26
|
-
return null;
|
|
27
|
-
const result = await installForCurrentPlatform();
|
|
28
|
-
if (result.installed) {
|
|
29
|
-
const dir = join(homedir(), ".unerr");
|
|
30
|
-
mkdirSync(dir, { recursive: true });
|
|
31
|
-
writeFileSync(SENTINEL_PATH, new Date().toISOString(), "utf-8");
|
|
32
|
-
}
|
|
33
|
-
return result;
|
|
34
|
-
}
|
|
35
|
-
export async function installForCurrentPlatform() {
|
|
36
|
-
const plat = process.platform;
|
|
37
|
-
if (plat === "darwin") {
|
|
38
|
-
const { installLaunchd } = await import("./platform-macos.js");
|
|
39
|
-
return installLaunchd();
|
|
40
|
-
}
|
|
41
|
-
if (plat === "linux") {
|
|
42
|
-
const { installSystemd } = await import("./platform-linux.js");
|
|
43
|
-
return installSystemd();
|
|
44
|
-
}
|
|
45
|
-
if (plat === "win32") {
|
|
46
|
-
const { installWindows } = await import("./platform-windows.js");
|
|
47
|
-
return installWindows();
|
|
48
|
-
}
|
|
49
|
-
return {
|
|
50
|
-
installed: false,
|
|
51
|
-
path: "",
|
|
52
|
-
error: `Unsupported platform: ${plat}. Auto-start is available on macOS, Linux, and Windows.`,
|
|
53
|
-
};
|
|
54
|
-
}
|
|
55
|
-
export async function uninstallForCurrentPlatform() {
|
|
56
|
-
const plat = process.platform;
|
|
57
|
-
if (plat === "darwin") {
|
|
58
|
-
const { uninstallLaunchd } = await import("./platform-macos.js");
|
|
59
|
-
return uninstallLaunchd();
|
|
60
|
-
}
|
|
61
|
-
if (plat === "linux") {
|
|
62
|
-
const { uninstallSystemd } = await import("./platform-linux.js");
|
|
63
|
-
return uninstallSystemd();
|
|
64
|
-
}
|
|
65
|
-
if (plat === "win32") {
|
|
66
|
-
const { uninstallWindows } = await import("./platform-windows.js");
|
|
67
|
-
return uninstallWindows();
|
|
68
|
-
}
|
|
69
|
-
return {
|
|
70
|
-
installed: false,
|
|
71
|
-
path: "",
|
|
72
|
-
error: `Unsupported platform: ${plat}`,
|
|
73
|
-
};
|
|
74
|
-
}
|
|
75
|
-
export async function getAutostartStatus() {
|
|
76
|
-
const plat = process.platform;
|
|
77
|
-
const sentinelExists = isAutostartInstalled();
|
|
78
|
-
if (plat === "darwin") {
|
|
79
|
-
const { getLaunchdStatus } = await import("./platform-macos.js");
|
|
80
|
-
return {
|
|
81
|
-
platform: "macOS (launchd)",
|
|
82
|
-
installed: sentinelExists,
|
|
83
|
-
sentinelExists,
|
|
84
|
-
details: getLaunchdStatus(),
|
|
85
|
-
};
|
|
86
|
-
}
|
|
87
|
-
if (plat === "linux") {
|
|
88
|
-
const { getSystemdStatus } = await import("./platform-linux.js");
|
|
89
|
-
return {
|
|
90
|
-
platform: "Linux (systemd)",
|
|
91
|
-
installed: sentinelExists,
|
|
92
|
-
sentinelExists,
|
|
93
|
-
details: getSystemdStatus(),
|
|
94
|
-
};
|
|
95
|
-
}
|
|
96
|
-
if (plat === "win32") {
|
|
97
|
-
const { isWindowsInstalled } = await import("./platform-windows.js");
|
|
98
|
-
return {
|
|
99
|
-
platform: "Windows (schtasks/startup)",
|
|
100
|
-
installed: sentinelExists,
|
|
101
|
-
sentinelExists,
|
|
102
|
-
details: { registered: isWindowsInstalled() },
|
|
103
|
-
};
|
|
104
|
-
}
|
|
105
|
-
return { platform: plat, installed: false, sentinelExists, details: {} };
|
|
106
|
-
}
|
|
107
|
-
/**
|
|
108
|
-
* Remove the sentinel to allow re-installation.
|
|
109
|
-
* Used by `unerr daemon autostart off`.
|
|
110
|
-
*/
|
|
111
|
-
export function removeSentinel() {
|
|
112
|
-
try {
|
|
113
|
-
if (existsSync(SENTINEL_PATH))
|
|
114
|
-
unlinkSync(SENTINEL_PATH);
|
|
115
|
-
}
|
|
116
|
-
catch {
|
|
117
|
-
// Best-effort
|
|
118
|
-
}
|
|
119
|
-
}
|
package/dist/daemon/bootstrap.js
DELETED
|
@@ -1,39 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* unerrd readiness polling — waits for the daemon socket to become reachable.
|
|
3
|
-
*
|
|
4
|
-
* Used by `daemon initialize` and `daemon start` after they spawn the process
|
|
5
|
-
* themselves. This module does NOT spawn anything — it only polls.
|
|
6
|
-
*
|
|
7
|
-
* Timeout: 5s polling at 100ms intervals. On timeout → throw.
|
|
8
|
-
*/
|
|
9
|
-
import { daemonSockPath, probeDaemon } from "./client.js";
|
|
10
|
-
const POLL_INTERVAL_MS = 100;
|
|
11
|
-
const WAIT_TIMEOUT_MS = 5_000;
|
|
12
|
-
/**
|
|
13
|
-
* Wait for `unerrd` to become reachable on its UDS socket.
|
|
14
|
-
* Returns immediately if already running.
|
|
15
|
-
*
|
|
16
|
-
* @returns The daemon socket path once it's confirmed reachable.
|
|
17
|
-
* @throws If the daemon is not reachable within WAIT_TIMEOUT_MS.
|
|
18
|
-
*/
|
|
19
|
-
export async function waitForDaemonReady() {
|
|
20
|
-
const sock = daemonSockPath();
|
|
21
|
-
// Fast path: daemon already running
|
|
22
|
-
if (await probeDaemon(sock)) {
|
|
23
|
-
return sock;
|
|
24
|
-
}
|
|
25
|
-
// Poll until the socket is reachable or timeout
|
|
26
|
-
const deadline = Date.now() + WAIT_TIMEOUT_MS;
|
|
27
|
-
while (Date.now() < deadline) {
|
|
28
|
-
await sleep(POLL_INTERVAL_MS);
|
|
29
|
-
if (await probeDaemon(sock)) {
|
|
30
|
-
return sock;
|
|
31
|
-
}
|
|
32
|
-
}
|
|
33
|
-
throw new Error(`unerrd did not become reachable within ${WAIT_TIMEOUT_MS / 1000}s. Check logs: ~/.unerr/logs/unerrd.log`);
|
|
34
|
-
}
|
|
35
|
-
/** @deprecated Use waitForDaemonReady instead. Alias kept for test compatibility. */
|
|
36
|
-
export const ensureDaemonRunning = waitForDaemonReady;
|
|
37
|
-
function sleep(ms) {
|
|
38
|
-
return new Promise((resolve) => setTimeout(resolve, ms));
|
|
39
|
-
}
|