homegraph 1.5.0 → 1.5.2
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/bin/command-supervision.d.ts.map +1 -1
- package/dist/bin/command-supervision.js +7 -4
- package/dist/bin/command-supervision.js.map +1 -1
- package/dist/extraction/index.d.ts +1 -1
- package/dist/extraction/index.d.ts.map +1 -1
- package/dist/extraction/index.js +54 -43
- package/dist/extraction/index.js.map +1 -1
- package/dist/extraction/languages/arkts.d.ts +69 -12
- package/dist/extraction/languages/arkts.d.ts.map +1 -1
- package/dist/extraction/languages/arkts.js +739 -86
- package/dist/extraction/languages/arkts.js.map +1 -1
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +3 -2
- package/dist/index.js.map +1 -1
- package/dist/mcp/diff-impact.d.ts +131 -0
- package/dist/mcp/diff-impact.d.ts.map +1 -0
- package/dist/mcp/diff-impact.js +385 -0
- package/dist/mcp/diff-impact.js.map +1 -0
- package/dist/mcp/liveness-watchdog.d.ts +18 -0
- package/dist/mcp/liveness-watchdog.d.ts.map +1 -1
- package/dist/mcp/liveness-watchdog.js +134 -22
- package/dist/mcp/liveness-watchdog.js.map +1 -1
- package/dist/mcp/server-instructions.d.ts +1 -1
- package/dist/mcp/server-instructions.d.ts.map +1 -1
- package/dist/mcp/server-instructions.js +2 -0
- package/dist/mcp/server-instructions.js.map +1 -1
- package/dist/mcp/tools.d.ts +21 -5
- package/dist/mcp/tools.d.ts.map +1 -1
- package/dist/mcp/tools.js +305 -55
- package/dist/mcp/tools.js.map +1 -1
- package/dist/resolution/callback-synthesizer.d.ts +2 -1
- package/dist/resolution/callback-synthesizer.d.ts.map +1 -1
- package/dist/resolution/callback-synthesizer.js +153 -53
- package/dist/resolution/callback-synthesizer.js.map +1 -1
- package/dist/resolution/frameworks/arkts-entry.d.ts.map +1 -1
- package/dist/resolution/frameworks/arkts-entry.js +20 -9
- package/dist/resolution/frameworks/arkts-entry.js.map +1 -1
- package/dist/resolution/index.d.ts.map +1 -1
- package/dist/resolution/index.js +1 -1
- package/dist/resolution/index.js.map +1 -1
- package/dist/resolution/memory-budget.d.ts +33 -0
- package/dist/resolution/memory-budget.d.ts.map +1 -1
- package/dist/resolution/memory-budget.js +58 -0
- package/dist/resolution/memory-budget.js.map +1 -1
- package/dist/resolution/resolver-pool.d.ts +2 -0
- package/dist/resolution/resolver-pool.d.ts.map +1 -1
- package/dist/resolution/resolver-pool.js +4 -0
- package/dist/resolution/resolver-pool.js.map +1 -1
- package/dist/ui/shimmer-progress.d.ts.map +1 -1
- package/dist/ui/shimmer-progress.js +4 -1
- package/dist/ui/shimmer-progress.js.map +1 -1
- package/package.json +4 -5
- package/dist/arkts/ohos-api-index.d.ts +0 -15
- package/dist/arkts/ohos-api-index.d.ts.map +0 -1
- package/dist/arkts/ohos-api-index.js +0 -190
- package/dist/arkts/ohos-api-index.js.map +0 -1
- package/dist/arkts/ohos-sdk-input.d.ts +0 -36
- package/dist/arkts/ohos-sdk-input.d.ts.map +0 -1
- package/dist/arkts/ohos-sdk-input.js +0 -214
- package/dist/arkts/ohos-sdk-input.js.map +0 -1
- package/dist/extraction/languages/arkts-state-decorators.d.ts +0 -13
- package/dist/extraction/languages/arkts-state-decorators.d.ts.map +0 -1
- package/dist/extraction/languages/arkts-state-decorators.js +0 -26
- package/dist/extraction/languages/arkts-state-decorators.js.map +0 -1
- package/dist/extraction/languages/arkts-viewtree.d.ts +0 -24
- package/dist/extraction/languages/arkts-viewtree.d.ts.map +0 -1
- package/dist/extraction/languages/arkts-viewtree.js +0 -148
- package/dist/extraction/languages/arkts-viewtree.js.map +0 -1
- package/dist/extraction/languages/ohos-api-consumer.d.ts +0 -34
- package/dist/extraction/languages/ohos-api-consumer.d.ts.map +0 -1
- package/dist/extraction/languages/ohos-api-consumer.js +0 -283
- package/dist/extraction/languages/ohos-api-consumer.js.map +0 -1
- package/dist/reasoning/config.d.ts +0 -45
- package/dist/reasoning/config.d.ts.map +0 -1
- package/dist/reasoning/config.js +0 -171
- package/dist/reasoning/config.js.map +0 -1
- package/dist/reasoning/credentials.d.ts +0 -5
- package/dist/reasoning/credentials.d.ts.map +0 -1
- package/dist/reasoning/credentials.js +0 -83
- package/dist/reasoning/credentials.js.map +0 -1
- package/dist/reasoning/login.d.ts +0 -21
- package/dist/reasoning/login.d.ts.map +0 -1
- package/dist/reasoning/login.js +0 -85
- package/dist/reasoning/login.js.map +0 -1
- package/dist/reasoning/reasoner.d.ts +0 -43
- package/dist/reasoning/reasoner.d.ts.map +0 -1
- package/dist/reasoning/reasoner.js +0 -308
- package/dist/reasoning/reasoner.js.map +0 -1
- package/dist/spec/build/git-scanner.d.ts +0 -93
- package/dist/spec/build/git-scanner.d.ts.map +0 -1
- package/dist/spec/build/git-scanner.js +0 -254
- package/dist/spec/build/git-scanner.js.map +0 -1
- package/dist/spec/evolve/llm-client.d.ts +0 -50
- package/dist/spec/evolve/llm-client.d.ts.map +0 -1
- package/dist/spec/evolve/llm-client.js +0 -176
- package/dist/spec/evolve/llm-client.js.map +0 -1
- package/dist/spec/evolve/logic-checker.d.ts +0 -12
- package/dist/spec/evolve/logic-checker.d.ts.map +0 -1
- package/dist/spec/evolve/logic-checker.js +0 -24
- package/dist/spec/evolve/logic-checker.js.map +0 -1
- package/dist/spec/git-utils.d.ts +0 -8
- package/dist/spec/git-utils.d.ts.map +0 -1
- package/dist/spec/git-utils.js +0 -14
- package/dist/spec/git-utils.js.map +0 -1
- package/dist/spec/llm/index.d.ts +0 -3
- package/dist/spec/llm/index.d.ts.map +0 -1
- package/dist/spec/llm/index.js +0 -11
- package/dist/spec/llm/index.js.map +0 -1
- package/dist/spec/mine/clusterer.d.ts +0 -63
- package/dist/spec/mine/clusterer.d.ts.map +0 -1
- package/dist/spec/mine/clusterer.js +0 -904
- package/dist/spec/mine/clusterer.js.map +0 -1
- package/dist/spec/mine/progress-handler.d.ts +0 -22
- package/dist/spec/mine/progress-handler.d.ts.map +0 -1
- package/dist/spec/mine/progress-handler.js +0 -108
- package/dist/spec/mine/progress-handler.js.map +0 -1
- package/dist/spec/mine/progress.d.ts +0 -23
- package/dist/spec/mine/progress.d.ts.map +0 -1
- package/dist/spec/mine/progress.js +0 -12
- package/dist/spec/mine/progress.js.map +0 -1
- package/dist/spec/mining/diff-parser.d.ts +0 -33
- package/dist/spec/mining/diff-parser.d.ts.map +0 -1
- package/dist/spec/mining/diff-parser.js +0 -166
- package/dist/spec/mining/diff-parser.js.map +0 -1
- package/dist/spec/mining/git-scanner.d.ts +0 -103
- package/dist/spec/mining/git-scanner.d.ts.map +0 -1
- package/dist/spec/mining/git-scanner.js +0 -307
- package/dist/spec/mining/git-scanner.js.map +0 -1
- package/dist/spec/mining/pipeline.d.ts +0 -53
- package/dist/spec/mining/pipeline.d.ts.map +0 -1
- package/dist/spec/mining/pipeline.js +0 -178
- package/dist/spec/mining/pipeline.js.map +0 -1
- package/dist/spec/mining/scope-resolver.d.ts +0 -45
- package/dist/spec/mining/scope-resolver.d.ts.map +0 -1
- package/dist/spec/mining/scope-resolver.js +0 -103
- package/dist/spec/mining/scope-resolver.js.map +0 -1
- package/dist/spec/mining/spec-extractor.d.ts +0 -69
- package/dist/spec/mining/spec-extractor.d.ts.map +0 -1
- package/dist/spec/mining/spec-extractor.js +0 -369
- package/dist/spec/mining/spec-extractor.js.map +0 -1
- package/dist/spec/utils.d.ts +0 -155
- package/dist/spec/utils.d.ts.map +0 -1
- package/dist/spec/utils.js +0 -411
- package/dist/spec/utils.js.map +0 -1
- package/scripts/add-lang/bench.sh +0 -60
- package/scripts/add-lang/check-grammar.mjs +0 -75
- package/scripts/add-lang/dump-ast.mjs +0 -103
- package/scripts/add-lang/verify-extraction.mjs +0 -70
- package/scripts/agent-eval/ab-adoption.sh +0 -91
- package/scripts/agent-eval/ab-hook.sh +0 -86
- package/scripts/agent-eval/ab-impl.sh +0 -78
- package/scripts/agent-eval/ab-new-vs-baseline.sh +0 -102
- package/scripts/agent-eval/ab-sufficiency.sh +0 -78
- package/scripts/agent-eval/arms-F.sh +0 -21
- package/scripts/agent-eval/arms-matrix.sh +0 -37
- package/scripts/agent-eval/audit.sh +0 -68
- package/scripts/agent-eval/bench-readme.sh +0 -28
- package/scripts/agent-eval/bench-why-repo.sh +0 -22
- package/scripts/agent-eval/block-read-hook.sh +0 -19
- package/scripts/agent-eval/hook-settings.json +0 -15
- package/scripts/agent-eval/itrun.sh +0 -120
- package/scripts/agent-eval/offload-eval-3arm.sh +0 -72
- package/scripts/agent-eval/offload-eval-cost.mjs +0 -133
- package/scripts/agent-eval/offload-eval-effort.mjs +0 -108
- package/scripts/agent-eval/offload-eval-frontload-matrix.sh +0 -25
- package/scripts/agent-eval/offload-eval-frontload.sh +0 -47
- package/scripts/agent-eval/offload-eval-ground-truth.json +0 -18
- package/scripts/agent-eval/offload-eval-hook.mjs +0 -84
- package/scripts/agent-eval/offload-eval-judge.mjs +0 -103
- package/scripts/agent-eval/offload-eval-matrix.sh +0 -20
- package/scripts/agent-eval/offload-eval-metrics.mjs +0 -94
- package/scripts/agent-eval/offload-eval-refs1.sh +0 -50
- package/scripts/agent-eval/offload-eval-setup.sh +0 -24
- package/scripts/agent-eval/offload-eval-styles.sh +0 -72
- package/scripts/agent-eval/offload-eval-summarize.mjs +0 -68
- package/scripts/agent-eval/offload-eval.md +0 -76
- package/scripts/agent-eval/parse-arms.mjs +0 -116
- package/scripts/agent-eval/parse-bench-readme.mjs +0 -84
- package/scripts/agent-eval/parse-run.mjs +0 -45
- package/scripts/agent-eval/parse-session.mjs +0 -93
- package/scripts/agent-eval/probe-context.mjs +0 -21
- package/scripts/agent-eval/probe-explore.mjs +0 -40
- package/scripts/agent-eval/probe-node.mjs +0 -20
- package/scripts/agent-eval/probe-sweep.mjs +0 -119
- package/scripts/agent-eval/probe-trace.mjs +0 -20
- package/scripts/agent-eval/redirect-read-hook.sh +0 -38
- package/scripts/agent-eval/repro-concurrent-explore.mjs +0 -119
- package/scripts/agent-eval/repro-daemon-clients.mjs +0 -125
- package/scripts/agent-eval/run-agent.sh +0 -34
- package/scripts/agent-eval/run-all.sh +0 -75
- package/scripts/agent-eval/run-arms.sh +0 -56
- package/scripts/agent-eval/seq-matrix.mjs +0 -137
- package/scripts/build-bundle.sh +0 -123
- package/scripts/exp_boundary_eval/README.md +0 -247
- package/scripts/exp_boundary_eval/_test_mcp_chain.py +0 -78
- package/scripts/exp_boundary_eval/_test_stdin.py +0 -8
- package/scripts/exp_boundary_eval/_utils.py +0 -1116
- package/scripts/exp_boundary_eval/analyze.py +0 -1313
- package/scripts/exp_boundary_eval/deveco_arm.py +0 -519
- package/scripts/exp_boundary_eval/run_all.py +0 -378
- package/scripts/exp_boundary_eval/run_one.py +0 -165
- package/scripts/exp_boundary_eval/run_session.py +0 -158
- package/scripts/exp_boundary_eval/setup.py +0 -120
- package/scripts/exp_boundary_eval/win_mcp_launcher.py +0 -73
- package/scripts/exp_boundary_eval/win_mcp_stdio_wrap.js +0 -36
- package/scripts/exp_boundary_eval/win_node_launcher.py +0 -24
- package/scripts/extract-release-notes.mjs +0 -130
- package/scripts/local-install.sh +0 -41
- package/scripts/npm-sdk.js +0 -75
- package/scripts/npm-shim.js +0 -275
- package/scripts/ohos-sdk-publish.mjs +0 -133
- package/scripts/pack-npm.sh +0 -119
- package/scripts/prepare-release.mjs +0 -270
|
@@ -1,158 +0,0 @@
|
|
|
1
|
-
#!/usr/bin/env python3
|
|
2
|
-
"""Run multi-turn session experiment (5). Supports multiple agents via --agent."""
|
|
3
|
-
|
|
4
|
-
import os
|
|
5
|
-
import subprocess
|
|
6
|
-
import sys
|
|
7
|
-
import time
|
|
8
|
-
from pathlib import Path
|
|
9
|
-
from typing import Optional
|
|
10
|
-
|
|
11
|
-
import setup as setup_mod
|
|
12
|
-
from _utils import (OUTPUT_DIR, append_run_log, build_agent_cmd,
|
|
13
|
-
current_time_ms, find_experiment, get_agent, info, header,
|
|
14
|
-
log, parse_output, resolve_agent_model, run_monitored_subprocess,
|
|
15
|
-
subprocess_no_window_kwargs, write_json, warn)
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
def run_session(exp_id: str = "5", skip_permissions: bool = True,
|
|
19
|
-
agent_name: str = "",
|
|
20
|
-
results_dir: Optional[Path] = None,
|
|
21
|
-
state_dir: Optional[Path] = None) -> bool:
|
|
22
|
-
exp = find_experiment(exp_id)
|
|
23
|
-
if exp is None:
|
|
24
|
-
print(f"ERROR: Experiment {exp_id} not found")
|
|
25
|
-
return False
|
|
26
|
-
|
|
27
|
-
agent = get_agent(agent_name)
|
|
28
|
-
clone_path = os.environ.get("REPO_CLONE", "")
|
|
29
|
-
local_repo = os.environ.get("REPO_PATH", "")
|
|
30
|
-
if clone_path:
|
|
31
|
-
repo_root = Path(clone_path)
|
|
32
|
-
elif local_repo:
|
|
33
|
-
repo_root = Path(local_repo)
|
|
34
|
-
else:
|
|
35
|
-
print("ERROR: No repo. Use run_all.py to clone, or set REPO_PATH / REPO_CLONE.")
|
|
36
|
-
return False
|
|
37
|
-
max_turns = exp.get("max_turns_per_round", 25)
|
|
38
|
-
|
|
39
|
-
header("Experiment 5: Session Persistence & Context Decay")
|
|
40
|
-
info(f"Agent: {agent['name']} ({agent['binary']}) | Repo: {repo_root}")
|
|
41
|
-
info(f"Max turns/round: {max_turns} | Permissions: {'skip' if skip_permissions else 'ask'}")
|
|
42
|
-
|
|
43
|
-
session_id, _ = setup_mod.prepare_environment("5", "full", agent_name,
|
|
44
|
-
state_dir=state_dir)
|
|
45
|
-
log(f"Shared Session ID: {session_id}")
|
|
46
|
-
|
|
47
|
-
result_dir = results_dir / "5" if results_dir else OUTPUT_DIR / "5"
|
|
48
|
-
result_dir.mkdir(parents=True, exist_ok=True)
|
|
49
|
-
|
|
50
|
-
rounds_data = []
|
|
51
|
-
session_model = ""
|
|
52
|
-
deveco_session_id = ""
|
|
53
|
-
for round_num in range(1, 6):
|
|
54
|
-
ri = next((r for r in exp["rounds"] if r["round"] == round_num), None)
|
|
55
|
-
if not ri: continue
|
|
56
|
-
|
|
57
|
-
header(f"Round {round_num} of 5")
|
|
58
|
-
info(f"Title: {ri['title']}")
|
|
59
|
-
|
|
60
|
-
round_dir = result_dir / f"round_{round_num}"
|
|
61
|
-
round_dir.mkdir(parents=True, exist_ok=True)
|
|
62
|
-
(round_dir / "prompt.txt").write_text(ri["prompt"], encoding="utf-8")
|
|
63
|
-
|
|
64
|
-
start_ms = current_time_ms()
|
|
65
|
-
is_resume = round_num > 1
|
|
66
|
-
|
|
67
|
-
cmd = build_agent_cmd(agent, session_id, max_turns, ri["prompt"],
|
|
68
|
-
resume=is_resume, skip_permissions=skip_permissions,
|
|
69
|
-
repo_root=repo_root)
|
|
70
|
-
|
|
71
|
-
stream_path = round_dir / "stream_output.jsonl"
|
|
72
|
-
text_path = round_dir / "raw_output.txt"
|
|
73
|
-
track_hg = os.environ.get("EVAL_ARM", "") == "homegraph"
|
|
74
|
-
|
|
75
|
-
with open(stream_path, "w", encoding="utf-8") as stream_f:
|
|
76
|
-
result, mem = run_monitored_subprocess(
|
|
77
|
-
cmd, stdout=stream_f, stderr=subprocess.STDOUT, text=True,
|
|
78
|
-
track_homegraph=track_hg,
|
|
79
|
-
)
|
|
80
|
-
exit_code = result.returncode
|
|
81
|
-
|
|
82
|
-
end_ms = current_time_ms()
|
|
83
|
-
dur_ms = end_ms - start_ms
|
|
84
|
-
dur_s = f"{dur_ms / 1000:.1f}"
|
|
85
|
-
|
|
86
|
-
parser_name = agent.get("parser", "claude_stream_json")
|
|
87
|
-
stats = parse_output(stream_path, parser_name)
|
|
88
|
-
if not session_model:
|
|
89
|
-
session_model = resolve_agent_model(agent, stats)
|
|
90
|
-
if not deveco_session_id and stats.deveco_session_id:
|
|
91
|
-
deveco_session_id = stats.deveco_session_id
|
|
92
|
-
|
|
93
|
-
text_content = "\n".join(stats.text_lines)
|
|
94
|
-
text_path.write_text(text_content or "(no text output)", encoding="utf-8")
|
|
95
|
-
|
|
96
|
-
r2 = subprocess.run(
|
|
97
|
-
["git", "diff", "--name-only"], capture_output=True, text=True, cwd=repo_root,
|
|
98
|
-
**subprocess_no_window_kwargs(),
|
|
99
|
-
)
|
|
100
|
-
mod_files = len([l for l in r2.stdout.strip().split("\n") if l])
|
|
101
|
-
|
|
102
|
-
rr = {"round": round_num, "title": ri["title"], "duration_ms": dur_ms,
|
|
103
|
-
"duration_s": dur_s, "exit_code": exit_code,
|
|
104
|
-
"tool_calls": stats.tool_calls, "tool_names": stats.tool_names,
|
|
105
|
-
"files_read": list(set(stats.files_read)),
|
|
106
|
-
"files_edited": list(set(stats.files_edited)),
|
|
107
|
-
"assistant_messages": stats.assistant_messages,
|
|
108
|
-
"max_turns_hit": stats.max_turns_hit,
|
|
109
|
-
"modified_files": mod_files, "max_turns": max_turns,
|
|
110
|
-
"memory": mem.to_dict()}
|
|
111
|
-
write_json(round_dir / "round_results.json", rr)
|
|
112
|
-
rounds_data.append(rr)
|
|
113
|
-
|
|
114
|
-
info(f"Round {round_num} done: {dur_s}s | Tool calls: {stats.tool_calls} | "
|
|
115
|
-
f"Files read: {len(set(stats.files_read))} | Edited: {mod_files}")
|
|
116
|
-
if stats.max_turns_hit:
|
|
117
|
-
warn(f" Max turns ({max_turns}) hit in round {round_num}")
|
|
118
|
-
if round_num < 5:
|
|
119
|
-
time.sleep(2)
|
|
120
|
-
|
|
121
|
-
summary = {
|
|
122
|
-
"experiment_id": "5", "session_id": session_id, "agent": agent["name"],
|
|
123
|
-
"model": session_model,
|
|
124
|
-
"deveco_session_id": deveco_session_id,
|
|
125
|
-
"total_rounds": len(rounds_data),
|
|
126
|
-
"total_duration_ms": sum(r["duration_ms"] for r in rounds_data),
|
|
127
|
-
"total_tool_calls": sum(int(r["tool_calls"]) for r in rounds_data),
|
|
128
|
-
"memory_retention_curve": [
|
|
129
|
-
{"round": r["round"], "tool_calls": int(r["tool_calls"]),
|
|
130
|
-
"files_read": r.get("files_read", [])} for r in rounds_data
|
|
131
|
-
],
|
|
132
|
-
}
|
|
133
|
-
write_json(result_dir / "session_results.json", summary)
|
|
134
|
-
|
|
135
|
-
print("\nMemory Retention Curve:")
|
|
136
|
-
for r in rounds_data:
|
|
137
|
-
tc = int(r["tool_calls"])
|
|
138
|
-
fr = len(r.get("files_read", []))
|
|
139
|
-
print(f" Round {r['round']}: {'#' * (tc // 2)} ({tc} tool calls, {fr} files read)")
|
|
140
|
-
|
|
141
|
-
log(f"Results: {result_dir}/session_results.json")
|
|
142
|
-
return True
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
if __name__ == "__main__":
|
|
146
|
-
args = sys.argv[1:]
|
|
147
|
-
exp_id = args[0] if args else "5"
|
|
148
|
-
agent_name = ""
|
|
149
|
-
skip = True
|
|
150
|
-
i = 1
|
|
151
|
-
while i < len(args):
|
|
152
|
-
if args[i] == "--agent" and i + 1 < len(args):
|
|
153
|
-
agent_name = args[i + 1]; i += 2
|
|
154
|
-
elif args[i] == "--no-skip-permissions":
|
|
155
|
-
skip = False; i += 1
|
|
156
|
-
else:
|
|
157
|
-
i += 1
|
|
158
|
-
sys.exit(0 if run_session(exp_id, skip, agent_name) else 1)
|
|
@@ -1,120 +0,0 @@
|
|
|
1
|
-
#!/usr/bin/env python3
|
|
2
|
-
"""Prepare a clean repo for an experiment run. Agent-aware."""
|
|
3
|
-
|
|
4
|
-
import os
|
|
5
|
-
import subprocess
|
|
6
|
-
import sys
|
|
7
|
-
from datetime import datetime, timezone
|
|
8
|
-
from pathlib import Path
|
|
9
|
-
from typing import Optional, Tuple
|
|
10
|
-
|
|
11
|
-
from _utils import (append_run_log, error, generate_session_id,
|
|
12
|
-
get_agent, get_agent_version, git_output, git_ok, log,
|
|
13
|
-
shell_ok, verify_agent_binary, write_json)
|
|
14
|
-
from deveco_arm import git_clean_repo, write_deveco_project_config
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
def prepare_environment(exp_id: str, clean_mode: str = "full",
|
|
18
|
-
agent_name: str = "",
|
|
19
|
-
state_dir: Optional[Path] = None) -> Tuple[str, str]:
|
|
20
|
-
"""Prepare repo. Returns (session_id, agent_name)."""
|
|
21
|
-
clone_path = os.environ.get("REPO_CLONE", "")
|
|
22
|
-
is_clone = bool(clone_path and Path(clone_path).is_dir())
|
|
23
|
-
local_repo = os.environ.get("REPO_PATH", "")
|
|
24
|
-
if is_clone:
|
|
25
|
-
repo_root = Path(clone_path)
|
|
26
|
-
elif local_repo and Path(local_repo).is_dir():
|
|
27
|
-
repo_root = Path(local_repo)
|
|
28
|
-
else:
|
|
29
|
-
error("No repo available. Clone via run_all.py or set REPO_PATH / REPO_CLONE.")
|
|
30
|
-
sys.exit(1)
|
|
31
|
-
|
|
32
|
-
if state_dir is not None:
|
|
33
|
-
_state_dir = state_dir
|
|
34
|
-
else:
|
|
35
|
-
from _utils import LOG_DIR
|
|
36
|
-
_state_dir = LOG_DIR
|
|
37
|
-
|
|
38
|
-
if is_clone:
|
|
39
|
-
log(f"Using cloned repo: {repo_root}")
|
|
40
|
-
else:
|
|
41
|
-
log(f"Using local repo: {repo_root}")
|
|
42
|
-
|
|
43
|
-
os.chdir(repo_root)
|
|
44
|
-
|
|
45
|
-
# Agent config
|
|
46
|
-
agent = get_agent(agent_name)
|
|
47
|
-
binary = agent["binary"]
|
|
48
|
-
|
|
49
|
-
# Step 1: Clean repo
|
|
50
|
-
arm = os.environ.get("EVAL_ARM", "baseline")
|
|
51
|
-
if clean_mode == "full":
|
|
52
|
-
log("Restoring repo to clean state...")
|
|
53
|
-
branch = os.environ.get("BRANCH", "weekly_20260601")
|
|
54
|
-
shell_ok(f"git checkout {branch}")
|
|
55
|
-
git_ok(["checkout", "."], cwd=repo_root)
|
|
56
|
-
git_clean_repo(repo_root, arm)
|
|
57
|
-
|
|
58
|
-
current_branch = git_output(["branch", "--show-current"], cwd=repo_root)
|
|
59
|
-
commit = git_output(["rev-parse", "--short", "HEAD"], cwd=repo_root)
|
|
60
|
-
log(f"Branch: {current_branch} | Commit: {commit}")
|
|
61
|
-
|
|
62
|
-
# Step 1b: DevEco A/B project config (.deveco/deveco.jsonc)
|
|
63
|
-
if agent.get("binary") == "deveco":
|
|
64
|
-
write_deveco_project_config(repo_root, arm)
|
|
65
|
-
|
|
66
|
-
# Step 1c: Index rebuilt once at homegraph suite start (run_all / prebuild).
|
|
67
|
-
# Per-experiment git reset restores the same commit — no second index here.
|
|
68
|
-
# Step 2: Clear CLAUDE.md
|
|
69
|
-
claude_md = repo_root / "CLAUDE.md"
|
|
70
|
-
if claude_md.exists() and claude_md.stat().st_size > 0:
|
|
71
|
-
backup_dir = _state_dir / "claude_md_backups"
|
|
72
|
-
backup_dir.mkdir(parents=True, exist_ok=True)
|
|
73
|
-
ts = datetime.now().strftime("%Y%m%d_%H%M%S")
|
|
74
|
-
backup_path = backup_dir / f"CLAUDE.md.{ts}"
|
|
75
|
-
backup_path.write_text(claude_md.read_text())
|
|
76
|
-
log(f"Backed up CLAUDE.md → {backup_path}")
|
|
77
|
-
with open(claude_md, "w") as f:
|
|
78
|
-
f.write("")
|
|
79
|
-
log("Cleared CLAUDE.md")
|
|
80
|
-
|
|
81
|
-
for f in [".cursorrules", ".cursor/rules"]:
|
|
82
|
-
p = repo_root / f
|
|
83
|
-
if p.exists():
|
|
84
|
-
p.unlink()
|
|
85
|
-
|
|
86
|
-
# Step 3: Session ID
|
|
87
|
-
session_id = generate_session_id()
|
|
88
|
-
(_state_dir / f"session_{exp_id}.txt").write_text(session_id)
|
|
89
|
-
log(f"Session ID: {session_id}")
|
|
90
|
-
|
|
91
|
-
# Step 4: Verify agent binary
|
|
92
|
-
ok, resolved, err_msg = verify_agent_binary(agent)
|
|
93
|
-
if not ok:
|
|
94
|
-
error(err_msg)
|
|
95
|
-
sys.exit(1)
|
|
96
|
-
binary = resolved
|
|
97
|
-
agent_ver = get_agent_version(agent)
|
|
98
|
-
log(f"Agent: {agent['name']} ({binary}) v{agent_ver}")
|
|
99
|
-
|
|
100
|
-
# Step 5: Record state
|
|
101
|
-
write_json(_state_dir / exp_id / "state.json", {
|
|
102
|
-
"experiment_id": exp_id, "session_id": session_id,
|
|
103
|
-
"started_at": datetime.now(timezone.utc).isoformat(),
|
|
104
|
-
"clean_mode": clean_mode, "agent": agent["name"],
|
|
105
|
-
"agent_version": agent_ver, "repo_root": str(repo_root),
|
|
106
|
-
"git_branch": git_output(["branch", "--show-current"], cwd=repo_root),
|
|
107
|
-
"git_commit": git_output(["rev-parse", "HEAD"], cwd=repo_root),
|
|
108
|
-
})
|
|
109
|
-
|
|
110
|
-
append_run_log(_state_dir, f"Setup complete for {exp_id}: session={session_id}, agent={agent['name']}")
|
|
111
|
-
log("Setup complete.")
|
|
112
|
-
return session_id, agent["name"]
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
if __name__ == "__main__":
|
|
116
|
-
exp_id = sys.argv[1] if len(sys.argv) > 1 else "all"
|
|
117
|
-
clean_mode = sys.argv[2] if len(sys.argv) > 2 else "full"
|
|
118
|
-
agent_name = sys.argv[3] if len(sys.argv) > 3 else ""
|
|
119
|
-
sid, _ = prepare_environment(exp_id, clean_mode, agent_name)
|
|
120
|
-
print(sid)
|
|
@@ -1,73 +0,0 @@
|
|
|
1
|
-
#!/usr/bin/env python3
|
|
2
|
-
"""Windows MCP entry: pythonw (no window) → hidden node child; stdio inherited from DevEco."""
|
|
3
|
-
|
|
4
|
-
from __future__ import annotations
|
|
5
|
-
|
|
6
|
-
import os
|
|
7
|
-
import shutil
|
|
8
|
-
import subprocess
|
|
9
|
-
import sys
|
|
10
|
-
|
|
11
|
-
_CREATE_NO_WINDOW = getattr(subprocess, "CREATE_NO_WINDOW", 0x08000000)
|
|
12
|
-
|
|
13
|
-
# Applied here so MCP works even if DevEco ignores deveco.jsonc "environment".
|
|
14
|
-
_MCP_ENV_DEFAULTS = {
|
|
15
|
-
"HOMEGRAPH_NO_DAEMON": "1",
|
|
16
|
-
"HOMEGRAPH_NO_WATCHDOG": "1",
|
|
17
|
-
"HOMEGRAPH_WASM_RELAUNCHED": "1",
|
|
18
|
-
}
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
def _resolve_node() -> str:
|
|
22
|
-
env_node = os.environ.get("HOMEGRAPH_NODE", "").strip()
|
|
23
|
-
if env_node:
|
|
24
|
-
return env_node
|
|
25
|
-
found = shutil.which("node")
|
|
26
|
-
if not found:
|
|
27
|
-
print("node not found on PATH", file=sys.stderr)
|
|
28
|
-
sys.exit(127)
|
|
29
|
-
return found
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
def _node_argv(raw: list[str]) -> list[str]:
|
|
33
|
-
"""Ensure --liftoff-only is on the node command line (before homegraph.js)."""
|
|
34
|
-
node = _resolve_node()
|
|
35
|
-
args = list(raw)
|
|
36
|
-
if "--liftoff-only" not in args:
|
|
37
|
-
args.insert(0, "--liftoff-only")
|
|
38
|
-
return [node, *args]
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
def _hidden_call(argv: list[str], env: dict) -> int:
|
|
42
|
-
si = subprocess.STARTUPINFO()
|
|
43
|
-
si.dwFlags |= subprocess.STARTF_USESHOWWINDOW
|
|
44
|
-
si.wShowWindow = 0
|
|
45
|
-
return subprocess.call(
|
|
46
|
-
argv,
|
|
47
|
-
env=env,
|
|
48
|
-
stdin=sys.stdin,
|
|
49
|
-
stdout=sys.stdout,
|
|
50
|
-
stderr=sys.stderr,
|
|
51
|
-
creationflags=_CREATE_NO_WINDOW,
|
|
52
|
-
startupinfo=si,
|
|
53
|
-
)
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
def main() -> int:
|
|
57
|
-
if len(sys.argv) < 2:
|
|
58
|
-
print(
|
|
59
|
-
"usage: win_mcp_launcher.py [--liftoff-only] [--stack-size=N] homegraph.js …",
|
|
60
|
-
file=sys.stderr,
|
|
61
|
-
)
|
|
62
|
-
return 2
|
|
63
|
-
env = os.environ.copy()
|
|
64
|
-
for key, val in _MCP_ENV_DEFAULTS.items():
|
|
65
|
-
env.setdefault(key, val)
|
|
66
|
-
argv = _node_argv(sys.argv[1:])
|
|
67
|
-
if os.name == "nt":
|
|
68
|
-
return _hidden_call(argv, env)
|
|
69
|
-
return subprocess.call(argv, env=env)
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
if __name__ == "__main__":
|
|
73
|
-
raise SystemExit(main())
|
|
@@ -1,36 +0,0 @@
|
|
|
1
|
-
#!/usr/bin/env node
|
|
2
|
-
/**
|
|
3
|
-
* Windows MCP launcher: spawn homegraph with windowsHide while keeping stdio
|
|
4
|
-
* connected to DevEco (pythonw + Python subprocess breaks JSON-RPC pipes).
|
|
5
|
-
*
|
|
6
|
-
* Usage: node win_mcp_stdio_wrap.js [--stack-size=N] homegraph.js serve --mcp --path <repo>
|
|
7
|
-
*/
|
|
8
|
-
'use strict';
|
|
9
|
-
|
|
10
|
-
const cp = require('child_process');
|
|
11
|
-
|
|
12
|
-
const node = process.env.HOMEGRAPH_NODE || process.execPath;
|
|
13
|
-
const args = process.argv.slice(2);
|
|
14
|
-
if (args.length === 0) {
|
|
15
|
-
process.stderr.write('usage: node win_mcp_stdio_wrap.js [--stack-size=N] homegraph.js …\n');
|
|
16
|
-
process.exit(2);
|
|
17
|
-
}
|
|
18
|
-
|
|
19
|
-
const child = cp.spawn(node, args, {
|
|
20
|
-
stdio: 'inherit',
|
|
21
|
-
windowsHide: true,
|
|
22
|
-
env: process.env,
|
|
23
|
-
});
|
|
24
|
-
|
|
25
|
-
child.on('error', (err) => {
|
|
26
|
-
process.stderr.write(String(err) + '\n');
|
|
27
|
-
process.exit(1);
|
|
28
|
-
});
|
|
29
|
-
|
|
30
|
-
child.on('exit', (code, signal) => {
|
|
31
|
-
if (signal) {
|
|
32
|
-
process.kill(process.pid, signal);
|
|
33
|
-
return;
|
|
34
|
-
}
|
|
35
|
-
process.exit(code ?? 1);
|
|
36
|
-
});
|
|
@@ -1,24 +0,0 @@
|
|
|
1
|
-
#!/usr/bin/env python3
|
|
2
|
-
"""Windows: run node without a console window; inherit stdio (MCP / homegraph CLI)."""
|
|
3
|
-
|
|
4
|
-
from __future__ import annotations
|
|
5
|
-
|
|
6
|
-
import os
|
|
7
|
-
import subprocess
|
|
8
|
-
import sys
|
|
9
|
-
|
|
10
|
-
_CREATE_NO_WINDOW = getattr(subprocess, "CREATE_NO_WINDOW", 0x08000000)
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
def main() -> int:
|
|
14
|
-
if len(sys.argv) < 2:
|
|
15
|
-
print("usage: win_node_launcher.py [--stack-size=N] script.js …", file=sys.stderr)
|
|
16
|
-
return 2
|
|
17
|
-
node = os.environ.get("HOMEGRAPH_NODE", "node")
|
|
18
|
-
cmd = [node, *sys.argv[1:]]
|
|
19
|
-
flags = _CREATE_NO_WINDOW if os.name == "nt" else 0
|
|
20
|
-
return subprocess.call(cmd, creationflags=flags)
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
if __name__ == "__main__":
|
|
24
|
-
raise SystemExit(main())
|
|
@@ -1,130 +0,0 @@
|
|
|
1
|
-
#!/usr/bin/env node
|
|
2
|
-
/**
|
|
3
|
-
* Extract a release-notes block from CHANGELOG.md for a given version
|
|
4
|
-
* (or unwrap text supplied on stdin), then join hard-wrapped paragraphs.
|
|
5
|
-
*
|
|
6
|
-
* Why: GitHub renders release-note Markdown with GFM hard breaks, so
|
|
7
|
-
* every `\n` becomes `<br>`. The CHANGELOG is hard-wrapped at ~75
|
|
8
|
-
* chars for readable diffs, which then renders as awkward visible
|
|
9
|
-
* line breaks on the release page. This script joins indented
|
|
10
|
-
* continuation lines into a single line per bullet so the GFM
|
|
11
|
-
* renderer produces clean paragraphs.
|
|
12
|
-
*
|
|
13
|
-
* Repo-level CHANGELOG.md viewing is unaffected (CommonMark treats
|
|
14
|
-
* newlines as spaces there).
|
|
15
|
-
*
|
|
16
|
-
* Usage:
|
|
17
|
-
* extract-release-notes.mjs <version> # read CHANGELOG.md
|
|
18
|
-
* extract-release-notes.mjs --stdin # read from stdin (any text)
|
|
19
|
-
*/
|
|
20
|
-
|
|
21
|
-
import { readFileSync } from 'fs';
|
|
22
|
-
|
|
23
|
-
const arg = process.argv[2];
|
|
24
|
-
if (!arg) {
|
|
25
|
-
console.error('usage: extract-release-notes.mjs <version> | --stdin');
|
|
26
|
-
process.exit(1);
|
|
27
|
-
}
|
|
28
|
-
|
|
29
|
-
let block;
|
|
30
|
-
if (arg === '--stdin') {
|
|
31
|
-
block = readFileSync(0, 'utf8').replace(/\r\n?/g, '\n').split('\n');
|
|
32
|
-
} else {
|
|
33
|
-
const version = arg;
|
|
34
|
-
const escaped = version.replace(/\./g, '\\.');
|
|
35
|
-
const headerRe = new RegExp(`^## \\[${escaped}\\]`);
|
|
36
|
-
const anyHeaderRe = /^## \[/;
|
|
37
|
-
const lines = readFileSync('CHANGELOG.md', 'utf8').split('\n');
|
|
38
|
-
const start = lines.findIndex((l) => headerRe.test(l));
|
|
39
|
-
if (start === -1) {
|
|
40
|
-
console.error(`no '## [${version}]' entry found in CHANGELOG.md`);
|
|
41
|
-
process.exit(1);
|
|
42
|
-
}
|
|
43
|
-
const after = lines.findIndex((l, i) => i > start && anyHeaderRe.test(l));
|
|
44
|
-
block = lines.slice(start, after === -1 ? lines.length : after);
|
|
45
|
-
}
|
|
46
|
-
|
|
47
|
-
// Track a stack of `{ indent: number }` frames so a continuation line
|
|
48
|
-
// can attach to the right ancestor. Handles the post-nested-list
|
|
49
|
-
// continuation pattern:
|
|
50
|
-
//
|
|
51
|
-
// - top-level
|
|
52
|
-
// - nested
|
|
53
|
-
// back to top-level <- 2-space indent, joins the top-level bullet
|
|
54
|
-
const out = [];
|
|
55
|
-
let buf = '';
|
|
56
|
-
let stack = [];
|
|
57
|
-
|
|
58
|
-
function flushBuf() {
|
|
59
|
-
if (buf !== '') {
|
|
60
|
-
out.push(buf);
|
|
61
|
-
buf = '';
|
|
62
|
-
}
|
|
63
|
-
}
|
|
64
|
-
|
|
65
|
-
function leadingSpaces(s) {
|
|
66
|
-
const m = s.match(/^(\s*)/);
|
|
67
|
-
return m ? m[1].length : 0;
|
|
68
|
-
}
|
|
69
|
-
|
|
70
|
-
// Bullets: `-`, `*`, `digit.` only. `+` is intentionally excluded — the
|
|
71
|
-
// CHANGELOG uses literal `+` inline (`config + instructions`) and we
|
|
72
|
-
// don't want to misread those as nested bullets.
|
|
73
|
-
const listItemRe = /^(\s*)([-*]|\d+\.)\s+/;
|
|
74
|
-
const fenceRe = /^\s*```/;
|
|
75
|
-
|
|
76
|
-
let inFence = false;
|
|
77
|
-
|
|
78
|
-
for (const line of block) {
|
|
79
|
-
// Fenced code blocks: pass through verbatim, no joining.
|
|
80
|
-
if (fenceRe.test(line)) {
|
|
81
|
-
flushBuf();
|
|
82
|
-
stack = [];
|
|
83
|
-
out.push(line);
|
|
84
|
-
inFence = !inFence;
|
|
85
|
-
continue;
|
|
86
|
-
}
|
|
87
|
-
if (inFence) {
|
|
88
|
-
out.push(line);
|
|
89
|
-
continue;
|
|
90
|
-
}
|
|
91
|
-
if (/^\s*$/.test(line)) {
|
|
92
|
-
flushBuf();
|
|
93
|
-
out.push('');
|
|
94
|
-
continue;
|
|
95
|
-
}
|
|
96
|
-
if (/^#/.test(line)) {
|
|
97
|
-
flushBuf();
|
|
98
|
-
stack = [];
|
|
99
|
-
out.push(line);
|
|
100
|
-
continue;
|
|
101
|
-
}
|
|
102
|
-
const itemMatch = line.match(listItemRe);
|
|
103
|
-
if (itemMatch) {
|
|
104
|
-
flushBuf();
|
|
105
|
-
const indent = itemMatch[1].length;
|
|
106
|
-
while (stack.length > 0 && stack[stack.length - 1].indent >= indent) {
|
|
107
|
-
stack.pop();
|
|
108
|
-
}
|
|
109
|
-
stack.push({ indent });
|
|
110
|
-
buf = line;
|
|
111
|
-
continue;
|
|
112
|
-
}
|
|
113
|
-
if (/^\s/.test(line)) {
|
|
114
|
-
const indent = leadingSpaces(line);
|
|
115
|
-
while (stack.length > 1 && stack[stack.length - 1].indent >= indent) {
|
|
116
|
-
flushBuf();
|
|
117
|
-
stack.pop();
|
|
118
|
-
}
|
|
119
|
-
const trimmed = line.replace(/^\s+/, '');
|
|
120
|
-
buf = buf === '' ? trimmed : `${buf} ${trimmed}`;
|
|
121
|
-
continue;
|
|
122
|
-
}
|
|
123
|
-
flushBuf();
|
|
124
|
-
stack = [];
|
|
125
|
-
out.push(line);
|
|
126
|
-
}
|
|
127
|
-
flushBuf();
|
|
128
|
-
|
|
129
|
-
process.stdout.write(out.join('\n'));
|
|
130
|
-
if (!out[out.length - 1]?.endsWith('\n')) process.stdout.write('\n');
|
package/scripts/local-install.sh
DELETED
|
@@ -1,41 +0,0 @@
|
|
|
1
|
-
#!/usr/bin/env bash
|
|
2
|
-
# Build the current branch and link it as the global `homegraph` for
|
|
3
|
-
# hands-on testing. Replaces any existing global install for as long
|
|
4
|
-
# as the symlink is in place.
|
|
5
|
-
#
|
|
6
|
-
# Usage:
|
|
7
|
-
# ./scripts/local-install.sh # build + link
|
|
8
|
-
# ./scripts/local-install.sh --undo # unlink + restore the published version
|
|
9
|
-
|
|
10
|
-
set -euo pipefail
|
|
11
|
-
|
|
12
|
-
cd "$(dirname "$0")/.."
|
|
13
|
-
|
|
14
|
-
PKG=$(node -p "require('./package.json').name")
|
|
15
|
-
VERSION=$(node -p "require('./package.json').version")
|
|
16
|
-
BRANCH=$(git rev-parse --abbrev-ref HEAD)
|
|
17
|
-
|
|
18
|
-
if [ "${1:-}" = "--undo" ]; then
|
|
19
|
-
echo "→ unlinking ${PKG}"
|
|
20
|
-
npm unlink -g "${PKG}" >/dev/null 2>&1 || true
|
|
21
|
-
echo "→ reinstalling published ${PKG}"
|
|
22
|
-
npm install -g "${PKG}"
|
|
23
|
-
echo "done: global homegraph -> $(command -v homegraph)"
|
|
24
|
-
exit 0
|
|
25
|
-
fi
|
|
26
|
-
|
|
27
|
-
echo "→ building ${PKG} ${VERSION} (${BRANCH})"
|
|
28
|
-
npm run build
|
|
29
|
-
|
|
30
|
-
echo "→ linking globally"
|
|
31
|
-
npm link
|
|
32
|
-
|
|
33
|
-
LINKED=$(command -v homegraph || echo "(not on PATH)")
|
|
34
|
-
echo
|
|
35
|
-
echo "✓ global homegraph now points to this branch"
|
|
36
|
-
echo " binary: ${LINKED}"
|
|
37
|
-
echo " branch: ${BRANCH}"
|
|
38
|
-
echo " version: ${VERSION}"
|
|
39
|
-
echo
|
|
40
|
-
echo "To restore the published version:"
|
|
41
|
-
echo " ./scripts/local-install.sh --undo"
|
package/scripts/npm-sdk.js
DELETED
|
@@ -1,75 +0,0 @@
|
|
|
1
|
-
'use strict';
|
|
2
|
-
//
|
|
3
|
-
// Programmatic / embedded SDK entry for homegraph (issue #354).
|
|
4
|
-
//
|
|
5
|
-
// The CLI/MCP `bin` (npm-shim.js) execs the per-platform bundle's OWN Node 24 so
|
|
6
|
-
// the tool never depends on the user's runtime. Embedded library consumers are
|
|
7
|
-
// the opposite case: they already run their own Node and just want the compiled
|
|
8
|
-
// API — `require("homegraph")` returning the HomeGraph class et al.
|
|
9
|
-
//
|
|
10
|
-
// The compiled library + its production dependencies (web-tree-sitter,
|
|
11
|
-
// tree-sitter-wasms, …) ship INSIDE the per-platform bundle, at
|
|
12
|
-
// homegraph-<platform>-<arch>/lib/dist/index.js
|
|
13
|
-
// (with the deps in the sibling lib/node_modules). Re-exporting that bundle keeps
|
|
14
|
-
// the main package thin — no second 50 MB copy of the grammars — while making the
|
|
15
|
-
// SDK work in the consumer's process. Types are a separate concern: the main
|
|
16
|
-
// package ships its own dist/**/*.d.ts tree (pointed at by `types`), built from
|
|
17
|
-
// the same release so it can never skew from the runtime it re-exports.
|
|
18
|
-
//
|
|
19
|
-
// node:sqlite (Node >= 22.5) is required to OPEN a graph, but only lazily inside
|
|
20
|
-
// the SQLite adapter — so loading this module is safe on older Node, and the
|
|
21
|
-
// node:sqlite requirement surfaces with an actionable error only when a DB is
|
|
22
|
-
// actually opened. Heavy extraction additionally wants the bundled launcher's
|
|
23
|
-
// --liftoff-only flag (the WASM Zone-OOM guard, issues #293/#298); an embedded
|
|
24
|
-
// host that drives large indexing should pass that flag to its own Node.
|
|
25
|
-
|
|
26
|
-
var path = require('path');
|
|
27
|
-
var os = require('os');
|
|
28
|
-
var fs = require('fs');
|
|
29
|
-
|
|
30
|
-
var target = process.platform + '-' + process.arch; // e.g. darwin-arm64, linux-x64
|
|
31
|
-
var pkg = 'homegraph-' + target;
|
|
32
|
-
|
|
33
|
-
module.exports = require(resolveLibrary());
|
|
34
|
-
|
|
35
|
-
// Locate the compiled library entry inside the installed per-platform bundle.
|
|
36
|
-
// Throws an actionable error (rather than a bare MODULE_NOT_FOUND) when no bundle
|
|
37
|
-
// is present, so an embedded consumer knows exactly what to install.
|
|
38
|
-
function resolveLibrary() {
|
|
39
|
-
// 1) The npm-installed optional dependency — the normal case.
|
|
40
|
-
try {
|
|
41
|
-
return require.resolve(pkg + '/lib/dist/index.js');
|
|
42
|
-
} catch (e) {
|
|
43
|
-
/* fall through to the self-healed cache */
|
|
44
|
-
}
|
|
45
|
-
|
|
46
|
-
// 2) A bundle the CLI shim self-healed from GitHub Releases into the cache
|
|
47
|
-
// (issue #303). Same node/lib/bin layout as the npm package. We only REUSE a
|
|
48
|
-
// cached bundle here — unlike the CLI shim we never trigger a network
|
|
49
|
-
// download from inside require(), which must stay synchronous and cheap.
|
|
50
|
-
var cached = cachedLibrary();
|
|
51
|
-
if (cached) return cached;
|
|
52
|
-
|
|
53
|
-
throw new Error(
|
|
54
|
-
'homegraph: the programmatic API is unavailable because the platform bundle\n' +
|
|
55
|
-
'(' + pkg + ') is not installed.\n' +
|
|
56
|
-
'The compiled library ships inside that per-platform optional dependency.\n' +
|
|
57
|
-
'Fixes:\n' +
|
|
58
|
-
' - install from the official npm registry so the matching bundle is fetched:\n' +
|
|
59
|
-
' npm i homegraph --registry=https://registry.npmjs.org\n' +
|
|
60
|
-
' - or run the CLI once (e.g. `npx homegraph status`) to\n' +
|
|
61
|
-
' self-heal the bundle into ~/.homegraph, then require() will find it.'
|
|
62
|
-
);
|
|
63
|
-
}
|
|
64
|
-
|
|
65
|
-
function cachedLibrary() {
|
|
66
|
-
try {
|
|
67
|
-
var version = require(path.join(__dirname, 'package.json')).version;
|
|
68
|
-
var base = process.env.HOMEGRAPH_INSTALL_DIR || path.join(os.homedir(), '.homegraph');
|
|
69
|
-
var lib = path.join(base, 'bundles', target + '-' + version, 'lib', 'dist', 'index.js');
|
|
70
|
-
if (fs.existsSync(lib)) return lib;
|
|
71
|
-
} catch (e) {
|
|
72
|
-
/* no readable cache → caller reports the install guidance */
|
|
73
|
-
}
|
|
74
|
-
return null;
|
|
75
|
-
}
|