File size: 4,432 Bytes
526950e
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
"""First-time machine setup: persist role + external paths + FERPA_DATA_ROOT.

Writes two files:
  - paths.json (user-config dir, machine-scoped; OUTSIDE the repo)
  - .me-hub/local-state.json (in-repo, gitignored)

The role declaration is explicit, never inferred from path existence.
"""
from __future__ import annotations

import json
import os
import socket
from pathlib import Path
from typing import Optional

from lib.local_state import SCHEMA_VERSION as LOCAL_STATE_SCHEMA_VERSION
from lib.paths import default_paths_json_path, repo_root as _repo_root_real


_PATHS_JSON_SCHEMA_VERSION = 1


class MissingMaintainerPathError(RuntimeError):
    pass


class FerpaInsideRepoError(RuntimeError):
    pass


def _paths_json_path() -> Path:
    return default_paths_json_path()


def _local_state_path() -> Path:
    return _repo_root_real() / ".me-hub" / "local-state.json"


def _repo_root() -> Path:
    return _repo_root_real()


def configure(
    *,
    declared_role: str,
    box_togo_path: Optional[Path],
    tolk_technology_path: Optional[Path],
    ferpa_data_root: Path,
    machine: str,
) -> None:
    """Persist the configuration for this machine."""
    if declared_role not in ("maintainer", "faculty"):
        raise ValueError(
            f"declared_role must be 'maintainer' or 'faculty', got {declared_role!r}"
        )

    if declared_role == "maintainer":
        if box_togo_path is None or not box_togo_path.exists():
            raise MissingMaintainerPathError("maintainer mode requires box_togo_path")
        if tolk_technology_path is None or not tolk_technology_path.exists():
            raise MissingMaintainerPathError(
                "maintainer mode requires tolk_technology_path"
            )

    repo = _repo_root()
    try:
        ferpa_data_root.resolve().relative_to(repo.resolve())
    except ValueError:
        pass
    else:
        raise FerpaInsideRepoError(
            f"FERPA_DATA_ROOT must be OUTSIDE the working tree "
            f"(got {ferpa_data_root}, which is inside {repo})"
        )

    ferpa_data_root.mkdir(parents=True, exist_ok=True)

    pj = _paths_json_path()
    pj.parent.mkdir(parents=True, exist_ok=True)
    pj.write_text(
        json.dumps(
            {
                "schema_version": _PATHS_JSON_SCHEMA_VERSION,
                "declared_role": declared_role,
                "box_togo_path": str(box_togo_path) if box_togo_path else None,
                "tolk_technology_path": str(tolk_technology_path) if tolk_technology_path else None,
                "ferpa_data_root": str(ferpa_data_root),
            },
            indent=2,
            sort_keys=True,
        ),
        encoding="utf-8",
    )

    ls = _local_state_path()
    ls.parent.mkdir(parents=True, exist_ok=True)
    ls.write_text(
        json.dumps(
            {
                "schema_version": LOCAL_STATE_SCHEMA_VERSION,
                "machine": machine,
                "declared_role": declared_role,
                "last_local_refresh_utc": None,
                "last_seen_state_commit": None,
            },
            indent=2,
            sort_keys=True,
        ),
        encoding="utf-8",
    )

    # Inform the maintainer about OPENAI_API_KEY (required for build-derived)
    if os.environ.get("OPENAI_API_KEY"):
        print("OK: OPENAI_API_KEY found in environment (will be used by build-derived).")
    else:
        print("")
        print("INFO: OPENAI_API_KEY not set. The build-derived pipeline requires it.")
        print("  - bash/zsh: export OPENAI_API_KEY=sk-...")
        print("  - powershell: $env:OPENAI_API_KEY = 'sk-...'")
        print("  - or set it in your shell profile so it persists across sessions.")

    # Inform the maintainer about ANTHROPIC_API_KEY (required for student-chat app)
    if os.environ.get("ANTHROPIC_API_KEY"):
        print("OK: ANTHROPIC_API_KEY found in environment (will be used by the chat app).")
    else:
        print("")
        print("INFO: ANTHROPIC_API_KEY not set. The student-chat application requires it.")
        print("  - bash/zsh: export ANTHROPIC_API_KEY=sk-ant-...")
        print("  - powershell: $env:ANTHROPIC_API_KEY = 'sk-ant-...'")
        print("  - or set it in your shell profile so it persists across sessions.")


def detect_machine() -> str:
    """Return socket.gethostname(). Called by bootstrap.{ps1,sh} to populate `machine`."""
    return socket.gethostname()