@kohala/devkit 0.1.0 → 0.1.1

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.
@@ -0,0 +1,125 @@
1
+ """Kohala tool SDK (local twin).
2
+
3
+ Your skill script talks to the Kohala runtime through these helpers. Locally
4
+ they call the emulator over a loopback RPC endpoint; on the hosted platform
5
+ the same functions talk to the real runtime. Your script does not change.
6
+
7
+ Uses only the Python standard library — no pip installs needed.
8
+
9
+ Available tools (each must also be listed in kohala.json -> toolAllowlist):
10
+
11
+ s3_put(key, body, category=None) store text in agent memory
12
+ s3_get(key_or_id) fetch a memory asset
13
+ s3_list(prefix=None, limit=None) list active memory assets
14
+ s3_delete(key_or_id) remove + deactivate an asset
15
+ http_post_json(url, body, headers=None) POST JSON to an external API
16
+ llm_complete(prompt, model=None) complete text with YOUR OWN LLM key
17
+ notify_send(channel, message) send a notification (trace, locally)
18
+ metrics_record(name, value, tags=None) record a metric (trace, locally)
19
+
20
+ Every helper raises KohalaToolError on failure — including TOOL_DENIED when
21
+ the tool is not in your allowlist, and PER_RUN_TOKEN_CAP when an LLM call
22
+ would cross your per-run token cap. Errors are loud on purpose.
23
+ """
24
+
25
+ import json
26
+ import os
27
+ import urllib.request
28
+
29
+
30
+ class KohalaToolError(Exception):
31
+ """A tool call failed. `code` is the platform's machine-readable code."""
32
+
33
+ def __init__(self, code, message):
34
+ super().__init__(f"{code}: {message}")
35
+ self.code = code
36
+ self.message = message
37
+
38
+
39
+ def _rpc(tool, args):
40
+ rpc_url = os.environ.get("KOHALA_RPC_URL")
41
+ if not rpc_url:
42
+ raise KohalaToolError(
43
+ "NO_RUNTIME",
44
+ "KOHALA_RPC_URL is not set. Run this script via `kohala run <agent> --local`, "
45
+ "not directly with python.",
46
+ )
47
+ payload = json.dumps({"tool": tool, "args": args}).encode("utf-8")
48
+ request = urllib.request.Request(
49
+ rpc_url,
50
+ data=payload,
51
+ headers={"Content-Type": "application/json"},
52
+ method="POST",
53
+ )
54
+ with urllib.request.urlopen(request) as response:
55
+ body = json.loads(response.read().decode("utf-8"))
56
+ if not body.get("ok"):
57
+ error = body.get("error") or {}
58
+ raise KohalaToolError(error.get("code", "UNKNOWN"), error.get("message", "tool call failed"))
59
+ return body.get("result")
60
+
61
+
62
+ def s3_put(key, body, category=None):
63
+ args = {"key": key, "body": body}
64
+ if category is not None:
65
+ args["category"] = category
66
+ return _rpc("s3.put", args)
67
+
68
+
69
+ def s3_get(key_or_id):
70
+ return _rpc("s3.get", {"keyOrId": key_or_id})
71
+
72
+
73
+ def s3_list(prefix=None, limit=None):
74
+ args = {}
75
+ if prefix is not None:
76
+ args["prefix"] = prefix
77
+ if limit is not None:
78
+ args["limit"] = limit
79
+ return _rpc("s3.list", args)
80
+
81
+
82
+ def s3_delete(key_or_id):
83
+ return _rpc("s3.delete", {"keyOrId": key_or_id})
84
+
85
+
86
+ def http_post_json(url, body, headers=None):
87
+ args = {"url": url, "body": body}
88
+ if headers is not None:
89
+ args["headers"] = headers
90
+ return _rpc("http.post_json", args)
91
+
92
+
93
+ def llm_complete(prompt, model=None):
94
+ args = {"prompt": prompt}
95
+ if model is not None:
96
+ args["model"] = model
97
+ return _rpc("llm.complete", args)
98
+
99
+
100
+ def notify_send(channel, message):
101
+ return _rpc("notify.send", {"channel": channel, "message": message})
102
+
103
+
104
+ def metrics_record(name, value, tags=None):
105
+ args = {"name": name, "value": value}
106
+ if tags is not None:
107
+ args["tags"] = tags
108
+ return _rpc("metrics.record", args)
109
+
110
+
111
+ def run_context():
112
+ """Info about the current shift, including repair-loop state.
113
+
114
+ Returns a dict with:
115
+ agent the agent name
116
+ run_id unique id of this shift
117
+ repair_attempt 0 on the first try, 1..2 on repair attempts
118
+ validator_feedback why validators failed last attempt (empty on first try)
119
+ """
120
+ return {
121
+ "agent": os.environ.get("KOHALA_AGENT", ""),
122
+ "run_id": os.environ.get("KOHALA_RUN_ID", ""),
123
+ "repair_attempt": int(os.environ.get("KOHALA_REPAIR_ATTEMPT", "0")),
124
+ "validator_feedback": os.environ.get("KOHALA_VALIDATOR_FEEDBACK", ""),
125
+ }
@@ -0,0 +1,39 @@
1
+ """weather-logger — fetch current conditions and store them in memory.
2
+
3
+ Demonstrates: http.post_json, s3.put, a freshness validator, and an
4
+ invariant validator (the output must mention "temperature").
5
+ """
6
+
7
+ import json
8
+ import sys
9
+
10
+ from _tools import s3_put, http_post_json, metrics_record, KohalaToolError
11
+
12
+
13
+ def main():
14
+ # Open-Meteo is free and needs no API key. (POST works for parity with
15
+ # the http.post_json tool; the API ignores the empty body.)
16
+ response = http_post_json(
17
+ "https://api.open-meteo.com/v1/forecast"
18
+ "?latitude=21.31&longitude=-157.86&current=temperature_2m,weather_code",
19
+ {},
20
+ )
21
+ if not response["ok"]:
22
+ print(f"weather API returned {response['status']}", file=sys.stderr)
23
+ raise SystemExit(1)
24
+
25
+ current = (response["json"] or {}).get("current", {})
26
+ temperature = current.get("temperature_2m")
27
+ if temperature is None:
28
+ print("weather API response had no temperature", file=sys.stderr)
29
+ raise SystemExit(1)
30
+
31
+ report = json.dumps({"temperature_c": temperature, "raw": current})
32
+ s3_put("weather/latest", report)
33
+ metrics_record("temperature_c", float(temperature))
34
+
35
+ print(f"stored weather/latest: temperature {temperature}°C")
36
+
37
+
38
+ if __name__ == "__main__":
39
+ main()
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@kohala/devkit",
3
- "version": "0.1.0",
3
+ "version": "0.1.1",
4
4
  "description": "Open-source CLI, local agent emulator, and open MCP memory server for Kohala agents. Build and run agents entirely on your own machine — then push the same agent to Kohala when you want it hosted.",
5
5
  "license": "MIT",
6
6
  "type": "module",
@@ -11,9 +11,14 @@
11
11
  "files": [
12
12
  "dist",
13
13
  "templates",
14
+ "docs",
15
+ "examples",
14
16
  "README.md",
15
17
  "LICENSE",
16
- "CHANGELOG.md"
18
+ "CHANGELOG.md",
19
+ "CONTRIBUTING.md",
20
+ "CODE_OF_CONDUCT.md",
21
+ "SECURITY.md"
17
22
  ],
18
23
  "engines": {
19
24
  "node": ">=20"