sakuraai 0.0.7 → 0.0.8

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.
Files changed (2) hide show
  1. package/dist/index.js +87 -1
  2. package/package.json +1 -1
package/dist/index.js CHANGED
@@ -37,7 +37,8 @@ function defaultConfig() {
37
37
  workspaces: [{ id: "local", name: "Local", slug: "local" }],
38
38
  projects: [],
39
39
  agentConfigs: [],
40
- daemon: { host: DEFAULT_HOST, port: DEFAULT_PORT }
40
+ daemon: { host: DEFAULT_HOST, port: DEFAULT_PORT },
41
+ pushTokens: []
41
42
  };
42
43
  }
43
44
  function loadConfig() {
@@ -946,6 +947,78 @@ var init_sessions = __esm({
946
947
  }
947
948
  });
948
949
 
950
+ // src/push.ts
951
+ function addPushToken(token) {
952
+ updateConfig((cfg) => {
953
+ if (!cfg.pushTokens) cfg.pushTokens = [];
954
+ if (!cfg.pushTokens.includes(token)) cfg.pushTokens.push(token);
955
+ });
956
+ }
957
+ async function sendPush(title, body, data = {}) {
958
+ const tokens = loadConfig().pushTokens ?? [];
959
+ if (tokens.length === 0) return;
960
+ const messages5 = tokens.map((to) => ({ to, title, body, sound: "default", data }));
961
+ try {
962
+ const res = await fetch(EXPO_PUSH_URL, {
963
+ method: "POST",
964
+ headers: { "Content-Type": "application/json", Accept: "application/json" },
965
+ body: JSON.stringify(messages5)
966
+ });
967
+ if (!res.ok) console.error(`[push] send failed: HTTP ${res.status}`);
968
+ } catch (e) {
969
+ console.error("[push] send error:", e);
970
+ }
971
+ }
972
+ function notifyTurnEnd(agent, sessionId, ok2, error, output) {
973
+ const label = AGENT_LABEL[agent] ?? agent;
974
+ const data = { sessionId, agent };
975
+ if (!ok2) {
976
+ const m2 = pick(ERROR)(label, error);
977
+ void sendPush(m2.title, m2.body, { ...data, kind: "error" });
978
+ return;
979
+ }
980
+ if (PERMISSION_RE.test(output)) {
981
+ const m2 = pick(PERMISSION)(label);
982
+ void sendPush(m2.title, m2.body, { ...data, kind: "permission" });
983
+ return;
984
+ }
985
+ const m = pick(DONE)(label);
986
+ void sendPush(m.title, m.body, { ...data, kind: "done" });
987
+ }
988
+ var EXPO_PUSH_URL, AGENT_LABEL, PERMISSION_RE, pick, DONE, PERMISSION, ERROR;
989
+ var init_push = __esm({
990
+ "src/push.ts"() {
991
+ "use strict";
992
+ init_config();
993
+ EXPO_PUSH_URL = "https://exp.host/--/api/v2/push/send";
994
+ AGENT_LABEL = {
995
+ claude: "Claude Code",
996
+ codex: "Codex",
997
+ opencode: "OpenCode"
998
+ };
999
+ PERMISSION_RE = /\b(need|needs|require[sd]?|asking for|waiting for|grant|allow|approve|approval|permission|authoriz)\b.{0,40}\b(permission|approval|access|to run|to proceed|to continue|your ok|confirm)\b/i;
1000
+ pick = (arr) => arr[Math.floor(Math.random() * arr.length)];
1001
+ DONE = [
1002
+ (a) => ({ title: "\u{1F389} Done & dusted", body: `${a} shipped your task. Go peek \u{1F440}` }),
1003
+ (a) => ({ title: "\u2728 Nailed it", body: `${a} just wrapped up \u2014 it's all yours.` }),
1004
+ (a) => ({ title: "\u{1F680} Mission complete", body: `${a} crushed it. Tap to review.` }),
1005
+ (a) => ({ title: "\u{1F3C1} Finished", body: `${a} finished the job \u{1F525}` }),
1006
+ (a) => ({ title: "\u{1F485} Ta-da", body: `${a} is done flexing on your codebase.` })
1007
+ ];
1008
+ PERMISSION = [
1009
+ (a) => ({ title: "\u{1F510} Permission, please", body: `${a} needs your go-ahead to continue.` }),
1010
+ (a) => ({ title: "\u{1F64B} Tap to approve", body: `${a} is standing by for your OK.` }),
1011
+ (a) => ({ title: "\u23F8\uFE0F Hold up", body: `${a} paused \u2014 it wants your blessing first.` }),
1012
+ (a) => ({ title: "\u{1F6A6} Waiting on you", body: `${a} can't proceed without a green light.` })
1013
+ ];
1014
+ ERROR = [
1015
+ (a, e) => ({ title: "\u{1F4A5} Uh oh", body: e ? `${a}: ${e}` : `${a} hit a snag. Tap to see what happened.` }),
1016
+ (a, e) => ({ title: "\u{1FAE0} Something broke", body: e ? `${a} stumbled: ${e}` : `${a} stopped unexpectedly.` }),
1017
+ (a) => ({ title: "\u26A0\uFE0F Agent tripped", body: `${a} ran into an error. Better check on it.` })
1018
+ ];
1019
+ }
1020
+ });
1021
+
949
1022
  // src/runtime/registry.ts
950
1023
  var registry_exports = {};
951
1024
  __export(registry_exports, {
@@ -1414,6 +1487,7 @@ async function runTurn(sessionId, agent, prompt, images) {
1414
1487
  const result = await chat(sessionId, prompt, agent, onData, images);
1415
1488
  clearInterval(timer);
1416
1489
  emit({ type: "done", sessionId, agent, ok: result.ok, error: result.error });
1490
+ notifyTurnEnd(agent, sessionId, result.ok, result.error, stdoutAcc);
1417
1491
  return { ok: result.ok, error: result.error };
1418
1492
  }
1419
1493
  var ANSI, stripAnsi, LIVE_ID, POLL_MS;
@@ -1422,6 +1496,7 @@ var init_stream = __esm({
1422
1496
  "use strict";
1423
1497
  init_sessions();
1424
1498
  init_hub();
1499
+ init_push();
1425
1500
  ANSI = /\[[0-9;]*[a-zA-Z]/g;
1426
1501
  stripAnsi = (s) => s.replace(ANSI, "");
1427
1502
  LIVE_ID = "__live__";
@@ -1688,6 +1763,7 @@ var init_server = __esm({
1688
1763
  init_config();
1689
1764
  init_logger();
1690
1765
  init_sessions();
1766
+ init_push();
1691
1767
  init_registry();
1692
1768
  init_fsops();
1693
1769
  init_tailscale();
@@ -1795,6 +1871,16 @@ var init_server = __esm({
1795
1871
  ({ url }) => ok(listMachines(url.searchParams.get("onlineOnly") === "true"))
1796
1872
  );
1797
1873
  add("GET", "/sakura/projects", () => ok(listProjects()));
1874
+ add("POST", "/sakura/push-token", ({ body }) => {
1875
+ const token = String(body?.token ?? "");
1876
+ if (!token) return fail("bad_request", "token required");
1877
+ addPushToken(token);
1878
+ return ok({ registered: true });
1879
+ });
1880
+ add("POST", "/sakura/push-test", async () => {
1881
+ await sendPush("\u{1F338} Sakura says hi", "Push is locked in. Your agents will ping you here.", { kind: "test" });
1882
+ return ok({ sent: true });
1883
+ });
1798
1884
  add("GET", "/sakura/sessions", async ({ url }) => {
1799
1885
  const limit = Number(url.searchParams.get("limit")) || void 0;
1800
1886
  const agent = url.searchParams.get("agent") || void 0;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "sakuraai",
3
- "version": "0.0.7",
3
+ "version": "0.0.8",
4
4
  "description": "Sakura Agent CLI + local runtime for managing AI coding sessions (Claude Code, Codex, OpenCode) and reaching them privately from the Sakura mobile app over Tailscale",
5
5
  "type": "module",
6
6
  "main": "dist/index.js",