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.
- package/dist/index.js +87 -1
- 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.
|
|
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",
|