codeksei 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.
Files changed (68) hide show
  1. package/LICENSE +661 -661
  2. package/README.en.md +109 -47
  3. package/README.md +79 -58
  4. package/bin/cyberboss.js +1 -1
  5. package/package.json +86 -86
  6. package/scripts/open_shared_wechat_thread.sh +77 -77
  7. package/scripts/open_wechat_thread.sh +108 -108
  8. package/scripts/shared-common.js +144 -144
  9. package/scripts/shared-open.js +14 -14
  10. package/scripts/shared-start.js +5 -5
  11. package/scripts/shared-status.js +27 -27
  12. package/scripts/show_shared_status.sh +45 -45
  13. package/scripts/start_shared_app_server.sh +52 -52
  14. package/scripts/start_shared_wechat.sh +94 -94
  15. package/scripts/timeline-screenshot.sh +14 -14
  16. package/src/adapters/channel/weixin/account-store.js +99 -99
  17. package/src/adapters/channel/weixin/api-v2.js +50 -50
  18. package/src/adapters/channel/weixin/api.js +169 -169
  19. package/src/adapters/channel/weixin/context-token-store.js +84 -84
  20. package/src/adapters/channel/weixin/index.js +618 -604
  21. package/src/adapters/channel/weixin/legacy.js +579 -566
  22. package/src/adapters/channel/weixin/media-mime.js +22 -22
  23. package/src/adapters/channel/weixin/media-receive.js +370 -370
  24. package/src/adapters/channel/weixin/media-send.js +102 -102
  25. package/src/adapters/channel/weixin/message-utils-v2.js +282 -282
  26. package/src/adapters/channel/weixin/message-utils.js +199 -199
  27. package/src/adapters/channel/weixin/redact.js +41 -41
  28. package/src/adapters/channel/weixin/reminder-queue-store.js +101 -101
  29. package/src/adapters/channel/weixin/sync-buffer-store.js +35 -35
  30. package/src/adapters/runtime/codex/events.js +215 -215
  31. package/src/adapters/runtime/codex/index.js +109 -104
  32. package/src/adapters/runtime/codex/message-utils.js +95 -95
  33. package/src/adapters/runtime/codex/model-catalog.js +106 -106
  34. package/src/adapters/runtime/codex/protocol-leak-monitor.js +75 -75
  35. package/src/adapters/runtime/codex/rpc-client.js +339 -339
  36. package/src/adapters/runtime/codex/session-store.js +286 -286
  37. package/src/app/channel-send-file-cli.js +57 -57
  38. package/src/app/diary-write-cli.js +236 -88
  39. package/src/app/note-sync-cli.js +2 -2
  40. package/src/app/reminder-write-cli.js +215 -210
  41. package/src/app/review-cli.js +7 -5
  42. package/src/app/system-checkin-poller.js +64 -64
  43. package/src/app/system-send-cli.js +129 -129
  44. package/src/app/timeline-event-cli.js +28 -25
  45. package/src/app/timeline-screenshot-cli.js +103 -100
  46. package/src/core/app.js +1763 -1763
  47. package/src/core/branding.js +2 -1
  48. package/src/core/command-registry.js +381 -369
  49. package/src/core/config.js +30 -14
  50. package/src/core/default-targets.js +163 -163
  51. package/src/core/durable-note-schema.js +9 -8
  52. package/src/core/instructions-template.js +17 -16
  53. package/src/core/note-sync.js +8 -7
  54. package/src/core/path-utils.js +54 -0
  55. package/src/core/project-radar.js +11 -10
  56. package/src/core/review.js +48 -50
  57. package/src/core/stream-delivery.js +1162 -983
  58. package/src/core/system-message-dispatcher.js +68 -68
  59. package/src/core/system-message-queue-store.js +128 -128
  60. package/src/core/thread-state-store.js +96 -96
  61. package/src/core/timeline-screenshot-queue-store.js +134 -134
  62. package/src/core/timezone.js +436 -0
  63. package/src/core/workspace-bootstrap.js +9 -1
  64. package/src/index.js +148 -146
  65. package/src/integrations/timeline/index.js +130 -74
  66. package/src/integrations/timeline/state-sync.js +240 -0
  67. package/templates/weixin-instructions.md +12 -38
  68. package/templates/weixin-operations.md +29 -31
@@ -72,34 +72,34 @@ function loadSharedEnv() {
72
72
  function ensureLogDir() {
73
73
  fs.mkdirSync(logDir, { recursive: true });
74
74
  }
75
-
76
- function isPidAlive(pid) {
77
- const numeric = Number(pid);
78
- if (!Number.isInteger(numeric) || numeric <= 0) {
79
- return false;
80
- }
81
- try {
82
- process.kill(numeric, 0);
83
- return true;
84
- } catch {
85
- return false;
86
- }
87
- }
88
-
89
- function readPidFile(filePath) {
90
- try {
91
- const raw = fs.readFileSync(filePath, "utf8").trim();
92
- return raw ? Number.parseInt(raw, 10) : 0;
93
- } catch {
94
- return 0;
95
- }
96
- }
97
-
98
- function writePidFile(filePath, pid) {
99
- fs.mkdirSync(path.dirname(filePath), { recursive: true });
100
- fs.writeFileSync(filePath, `${pid}\n`, "utf8");
101
- }
102
-
75
+
76
+ function isPidAlive(pid) {
77
+ const numeric = Number(pid);
78
+ if (!Number.isInteger(numeric) || numeric <= 0) {
79
+ return false;
80
+ }
81
+ try {
82
+ process.kill(numeric, 0);
83
+ return true;
84
+ } catch {
85
+ return false;
86
+ }
87
+ }
88
+
89
+ function readPidFile(filePath) {
90
+ try {
91
+ const raw = fs.readFileSync(filePath, "utf8").trim();
92
+ return raw ? Number.parseInt(raw, 10) : 0;
93
+ } catch {
94
+ return 0;
95
+ }
96
+ }
97
+
98
+ function writePidFile(filePath, pid) {
99
+ fs.mkdirSync(path.dirname(filePath), { recursive: true });
100
+ fs.writeFileSync(filePath, `${pid}\n`, "utf8");
101
+ }
102
+
103
103
  function removePidFileIfMatches(filePath, pid) {
104
104
  const current = readPidFile(filePath);
105
105
  if (current && current === pid) {
@@ -164,26 +164,26 @@ function findListeningPidByPort(targetPort) {
164
164
  return 0;
165
165
  }
166
166
  }
167
-
167
+
168
168
  function checkReadyz() {
169
169
  return new Promise((resolve) => {
170
- const req = http.get(
171
- {
172
- hostname: "127.0.0.1",
173
- port: Number(port),
174
- path: "/readyz",
175
- timeout: 500,
176
- },
177
- (res) => {
178
- res.resume();
179
- resolve(res.statusCode >= 200 && res.statusCode < 300);
180
- }
181
- );
182
- req.on("error", () => resolve(false));
183
- req.on("timeout", () => {
184
- req.destroy();
185
- resolve(false);
186
- });
170
+ const req = http.get(
171
+ {
172
+ hostname: "127.0.0.1",
173
+ port: Number(port),
174
+ path: "/readyz",
175
+ timeout: 500,
176
+ },
177
+ (res) => {
178
+ res.resume();
179
+ resolve(res.statusCode >= 200 && res.statusCode < 300);
180
+ }
181
+ );
182
+ req.on("error", () => resolve(false));
183
+ req.on("timeout", () => {
184
+ req.destroy();
185
+ resolve(false);
186
+ });
187
187
  });
188
188
  }
189
189
 
@@ -206,17 +206,17 @@ async function resolveReadyAppServerPid() {
206
206
 
207
207
  return pidFromFile && isPidAlive(pidFromFile) ? pidFromFile : 0;
208
208
  }
209
-
210
- async function waitForReadyz({ attempts = 10, delayMs = 300 } = {}) {
211
- for (let index = 0; index < attempts; index += 1) {
212
- if (await checkReadyz()) {
213
- return true;
214
- }
215
- await sleep(delayMs);
216
- }
217
- return false;
218
- }
219
-
209
+
210
+ async function waitForReadyz({ attempts = 10, delayMs = 300 } = {}) {
211
+ for (let index = 0; index < attempts; index += 1) {
212
+ if (await checkReadyz()) {
213
+ return true;
214
+ }
215
+ await sleep(delayMs);
216
+ }
217
+ return false;
218
+ }
219
+
220
220
  function openLogFile(filePath) {
221
221
  return fs.openSync(filePath, "a");
222
222
  }
@@ -558,8 +558,8 @@ async function ensureSharedAppServer() {
558
558
  logFile: appServerLogFile,
559
559
  env,
560
560
  });
561
- writePidFile(appServerPidFile, pid);
562
-
561
+ writePidFile(appServerPidFile, pid);
562
+
563
563
  const ready = await waitForReadyz();
564
564
  if (!ready) {
565
565
  throw new Error(`failed to start shared app-server; check ${appServerLogFile}`);
@@ -601,9 +601,9 @@ function ensureBridgeNotRunning() {
601
601
  const pidFromFile = readPidFile(bridgePidFile);
602
602
  if (pidFromFile && isPidAlive(pidFromFile)) {
603
603
  return pidFromFile;
604
- }
605
- if (pidFromFile) {
606
- fs.rmSync(bridgePidFile, { force: true });
604
+ }
605
+ if (pidFromFile) {
606
+ fs.rmSync(bridgePidFile, { force: true });
607
607
  }
608
608
  return 0;
609
609
  }
@@ -669,88 +669,88 @@ async function ensureManagedSupervisor({ intervalMinutes = 5 } = {}) {
669
669
  status: "started",
670
670
  };
671
671
  }
672
-
673
- function resolveCurrentAccountId() {
674
- if (!fs.existsSync(accountsDir)) {
675
- return "";
676
- }
677
- const entries = fs.readdirSync(accountsDir)
678
- .filter((name) => name.endsWith(".json") && !name.endsWith(".context-tokens.json"))
679
- .map((name) => {
680
- const fullPath = path.join(accountsDir, name);
681
- try {
682
- const parsed = JSON.parse(fs.readFileSync(fullPath, "utf8"));
683
- return {
684
- accountId: normalizeText(parsed?.accountId),
685
- savedAt: parseTimestamp(parsed?.savedAt),
686
- };
687
- } catch {
688
- return null;
689
- }
690
- })
691
- .filter(Boolean)
692
- .filter((entry) => entry.accountId);
693
- entries.sort((left, right) => right.savedAt - left.savedAt);
694
- return entries[0]?.accountId || "";
695
- }
696
-
697
- function resolveBoundThread(workspaceRoot) {
698
- if (!fs.existsSync(sessionFile)) {
699
- throw new Error(`session file not found: ${sessionFile}`);
700
- }
701
- const data = JSON.parse(fs.readFileSync(sessionFile, "utf8"));
702
- const currentAccountId = resolveCurrentAccountId();
703
- const bindings = Object.values(data.bindings || {})
704
- .filter((binding) => !currentAccountId || normalizeText(binding?.accountId) === currentAccountId)
705
- .sort((left, right) => parseTimestamp(right?.updatedAt) - parseTimestamp(left?.updatedAt));
706
-
707
- const normalizedWorkspaceRoot = normalizeText(workspaceRoot);
708
- const exact = bindings.find((binding) => getThreadId(binding, normalizedWorkspaceRoot));
709
- if (exact) {
710
- return {
711
- threadId: getThreadId(exact, normalizedWorkspaceRoot),
712
- workspaceRoot: normalizedWorkspaceRoot,
713
- };
714
- }
715
-
716
- const active = bindings.find((binding) => {
717
- const activeWorkspaceRoot = normalizeText(binding?.activeWorkspaceRoot);
718
- return activeWorkspaceRoot && getThreadId(binding, activeWorkspaceRoot);
719
- });
720
- if (active) {
721
- const activeWorkspaceRoot = normalizeText(active.activeWorkspaceRoot);
722
- return {
723
- threadId: getThreadId(active, activeWorkspaceRoot),
724
- workspaceRoot: activeWorkspaceRoot,
725
- };
726
- }
727
-
728
- throw new Error(`no bound WeChat thread found for workspace: ${workspaceRoot}`);
729
- }
730
-
731
- function getThreadId(binding, workspaceRoot) {
732
- if (!workspaceRoot) {
733
- return "";
734
- }
735
- const map = binding && typeof binding.threadIdByWorkspaceRoot === "object"
736
- ? binding.threadIdByWorkspaceRoot
737
- : {};
738
- return normalizeText(map[workspaceRoot]);
739
- }
740
-
741
- function parseTimestamp(value) {
742
- const parsed = Date.parse(normalizeText(value));
743
- return Number.isFinite(parsed) ? parsed : 0;
744
- }
745
-
746
- function normalizeText(value) {
747
- return typeof value === "string" ? value.trim() : "";
748
- }
749
-
750
- function sleep(ms) {
751
- return new Promise((resolve) => setTimeout(resolve, ms));
752
- }
753
-
672
+
673
+ function resolveCurrentAccountId() {
674
+ if (!fs.existsSync(accountsDir)) {
675
+ return "";
676
+ }
677
+ const entries = fs.readdirSync(accountsDir)
678
+ .filter((name) => name.endsWith(".json") && !name.endsWith(".context-tokens.json"))
679
+ .map((name) => {
680
+ const fullPath = path.join(accountsDir, name);
681
+ try {
682
+ const parsed = JSON.parse(fs.readFileSync(fullPath, "utf8"));
683
+ return {
684
+ accountId: normalizeText(parsed?.accountId),
685
+ savedAt: parseTimestamp(parsed?.savedAt),
686
+ };
687
+ } catch {
688
+ return null;
689
+ }
690
+ })
691
+ .filter(Boolean)
692
+ .filter((entry) => entry.accountId);
693
+ entries.sort((left, right) => right.savedAt - left.savedAt);
694
+ return entries[0]?.accountId || "";
695
+ }
696
+
697
+ function resolveBoundThread(workspaceRoot) {
698
+ if (!fs.existsSync(sessionFile)) {
699
+ throw new Error(`session file not found: ${sessionFile}`);
700
+ }
701
+ const data = JSON.parse(fs.readFileSync(sessionFile, "utf8"));
702
+ const currentAccountId = resolveCurrentAccountId();
703
+ const bindings = Object.values(data.bindings || {})
704
+ .filter((binding) => !currentAccountId || normalizeText(binding?.accountId) === currentAccountId)
705
+ .sort((left, right) => parseTimestamp(right?.updatedAt) - parseTimestamp(left?.updatedAt));
706
+
707
+ const normalizedWorkspaceRoot = normalizeText(workspaceRoot);
708
+ const exact = bindings.find((binding) => getThreadId(binding, normalizedWorkspaceRoot));
709
+ if (exact) {
710
+ return {
711
+ threadId: getThreadId(exact, normalizedWorkspaceRoot),
712
+ workspaceRoot: normalizedWorkspaceRoot,
713
+ };
714
+ }
715
+
716
+ const active = bindings.find((binding) => {
717
+ const activeWorkspaceRoot = normalizeText(binding?.activeWorkspaceRoot);
718
+ return activeWorkspaceRoot && getThreadId(binding, activeWorkspaceRoot);
719
+ });
720
+ if (active) {
721
+ const activeWorkspaceRoot = normalizeText(active.activeWorkspaceRoot);
722
+ return {
723
+ threadId: getThreadId(active, activeWorkspaceRoot),
724
+ workspaceRoot: activeWorkspaceRoot,
725
+ };
726
+ }
727
+
728
+ throw new Error(`no bound WeChat thread found for workspace: ${workspaceRoot}`);
729
+ }
730
+
731
+ function getThreadId(binding, workspaceRoot) {
732
+ if (!workspaceRoot) {
733
+ return "";
734
+ }
735
+ const map = binding && typeof binding.threadIdByWorkspaceRoot === "object"
736
+ ? binding.threadIdByWorkspaceRoot
737
+ : {};
738
+ return normalizeText(map[workspaceRoot]);
739
+ }
740
+
741
+ function parseTimestamp(value) {
742
+ const parsed = Date.parse(normalizeText(value));
743
+ return Number.isFinite(parsed) ? parsed : 0;
744
+ }
745
+
746
+ function normalizeText(value) {
747
+ return typeof value === "string" ? value.trim() : "";
748
+ }
749
+
750
+ function sleep(ms) {
751
+ return new Promise((resolve) => setTimeout(resolve, ms));
752
+ }
753
+
754
754
  module.exports = {
755
755
  rootDir,
756
756
  port,
@@ -30,17 +30,17 @@ async function main() {
30
30
  shell: false,
31
31
  windowsHide: true,
32
32
  });
33
-
34
- child.on("exit", (code, signal) => {
35
- if (signal) {
36
- process.kill(process.pid, signal);
37
- return;
38
- }
39
- process.exit(code ?? 0);
40
- });
41
- }
42
-
43
- main().catch((error) => {
44
- console.error(error.message || String(error));
45
- process.exit(1);
46
- });
33
+
34
+ child.on("exit", (code, signal) => {
35
+ if (signal) {
36
+ process.kill(process.pid, signal);
37
+ return;
38
+ }
39
+ process.exit(code ?? 0);
40
+ });
41
+ }
42
+
43
+ main().catch((error) => {
44
+ console.error(error.message || String(error));
45
+ process.exit(1);
46
+ });
@@ -34,8 +34,8 @@ async function main() {
34
34
  const supervisor = await ensureManagedSupervisor({ intervalMinutes: parseIntervalMinutes() });
35
35
  console.log(`shared supervisor ${supervisor.status} pid=${supervisor.pid}`);
36
36
  }
37
-
38
- main().catch((error) => {
39
- console.error(error.message || String(error));
40
- process.exit(1);
41
- });
37
+
38
+ main().catch((error) => {
39
+ console.error(error.message || String(error));
40
+ process.exit(1);
41
+ });
@@ -34,34 +34,34 @@ function printPidState(label, filePath, fallbackPid = 0) {
34
34
  if (!pid) {
35
35
  console.log(`${label}=missing`);
36
36
  return;
37
- }
38
- if (!isPidAlive(pid)) {
39
- console.log(`${label}=stale`);
40
- return;
41
- }
42
- console.log(`${label}=${pid}`);
43
- }
44
-
37
+ }
38
+ if (!isPidAlive(pid)) {
39
+ console.log(`${label}=stale`);
40
+ return;
41
+ }
42
+ console.log(`${label}=${pid}`);
43
+ }
44
+
45
45
  function checkReadyz() {
46
- return new Promise((resolve) => {
47
- const req = http.get(
48
- {
49
- hostname: "127.0.0.1",
50
- port: new URL(listenUrl).port,
51
- path: "/readyz",
52
- timeout: 600,
53
- },
54
- (res) => {
55
- res.resume();
56
- resolve(res.statusCode >= 200 && res.statusCode < 300);
57
- }
58
- );
59
- req.on("error", () => resolve(false));
60
- req.on("timeout", () => {
61
- req.destroy();
62
- resolve(false);
63
- });
64
- });
46
+ return new Promise((resolve) => {
47
+ const req = http.get(
48
+ {
49
+ hostname: "127.0.0.1",
50
+ port: new URL(listenUrl).port,
51
+ path: "/readyz",
52
+ timeout: 600,
53
+ },
54
+ (res) => {
55
+ res.resume();
56
+ resolve(res.statusCode >= 200 && res.statusCode < 300);
57
+ }
58
+ );
59
+ req.on("error", () => resolve(false));
60
+ req.on("timeout", () => {
61
+ req.destroy();
62
+ resolve(false);
63
+ });
64
+ });
65
65
  }
66
66
 
67
67
  function normalizeText(value) {
@@ -1,53 +1,53 @@
1
- #!/bin/zsh
2
- set -euo pipefail
3
-
1
+ #!/bin/zsh
2
+ set -euo pipefail
3
+
4
4
  PORT="${CODEKSEI_SHARED_PORT:-${CYBERBOSS_SHARED_PORT:-8765}}"
5
5
  LISTEN_URL="ws://127.0.0.1:${PORT}"
6
6
  STATE_DIR="${CODEKSEI_STATE_DIR:-${CYBERBOSS_STATE_DIR:-$HOME/.codeksei}}"
7
7
  if [[ ! -d "${STATE_DIR}" && -d "$HOME/.cyberboss" ]]; then
8
8
  STATE_DIR="$HOME/.cyberboss"
9
9
  fi
10
- LOG_DIR="${STATE_DIR}/logs"
11
- APP_SERVER_PID_FILE="${LOG_DIR}/shared-app-server.pid"
12
- WECHAT_PID_FILE="${LOG_DIR}/shared-wechat.pid"
13
- WECHAT_LOG_FILE="${LOG_DIR}/shared-wechat.log"
14
-
15
- function print_pid_state() {
16
- local label="$1"
17
- local pid_file="$2"
18
-
19
- if [[ -f "${pid_file}" ]]; then
20
- local pid
21
- pid="$(cat "${pid_file}" 2>/dev/null || true)"
22
- if [[ -n "${pid}" ]] && kill -0 "${pid}" 2>/dev/null; then
23
- echo "${label}=${pid}"
24
- return
25
- fi
26
- echo "${label}=stale"
27
- return
28
- fi
29
-
30
- echo "${label}=missing"
31
- }
32
-
33
- echo "listen=${LISTEN_URL}"
34
- print_pid_state "shared_app_server_pid" "${APP_SERVER_PID_FILE}"
10
+ LOG_DIR="${STATE_DIR}/logs"
11
+ APP_SERVER_PID_FILE="${LOG_DIR}/shared-app-server.pid"
12
+ WECHAT_PID_FILE="${LOG_DIR}/shared-wechat.pid"
13
+ WECHAT_LOG_FILE="${LOG_DIR}/shared-wechat.log"
14
+
15
+ function print_pid_state() {
16
+ local label="$1"
17
+ local pid_file="$2"
18
+
19
+ if [[ -f "${pid_file}" ]]; then
20
+ local pid
21
+ pid="$(cat "${pid_file}" 2>/dev/null || true)"
22
+ if [[ -n "${pid}" ]] && kill -0 "${pid}" 2>/dev/null; then
23
+ echo "${label}=${pid}"
24
+ return
25
+ fi
26
+ echo "${label}=stale"
27
+ return
28
+ fi
29
+
30
+ echo "${label}=missing"
31
+ }
32
+
33
+ echo "listen=${LISTEN_URL}"
34
+ print_pid_state "shared_app_server_pid" "${APP_SERVER_PID_FILE}"
35
35
  print_pid_state "shared_codeksei_pid" "${WECHAT_PID_FILE}"
36
36
  print_pid_state "shared_cyberboss_pid" "${WECHAT_PID_FILE}"
37
-
38
- if command -v curl >/dev/null 2>&1; then
39
- if curl -sf "http://127.0.0.1:${PORT}/readyz" >/dev/null; then
40
- echo "readyz=ok"
41
- else
42
- echo "readyz=down"
43
- fi
44
- fi
45
-
46
- if command -v lsof >/dev/null 2>&1; then
47
- lsof -nP -iTCP:"${PORT}" -sTCP:LISTEN || true
48
- fi
49
-
50
- if [[ -f "${WECHAT_LOG_FILE}" ]]; then
51
- echo "--- ${WECHAT_LOG_FILE} (tail) ---"
52
- tail -n 20 "${WECHAT_LOG_FILE}" || true
53
- fi
37
+
38
+ if command -v curl >/dev/null 2>&1; then
39
+ if curl -sf "http://127.0.0.1:${PORT}/readyz" >/dev/null; then
40
+ echo "readyz=ok"
41
+ else
42
+ echo "readyz=down"
43
+ fi
44
+ fi
45
+
46
+ if command -v lsof >/dev/null 2>&1; then
47
+ lsof -nP -iTCP:"${PORT}" -sTCP:LISTEN || true
48
+ fi
49
+
50
+ if [[ -f "${WECHAT_LOG_FILE}" ]]; then
51
+ echo "--- ${WECHAT_LOG_FILE} (tail) ---"
52
+ tail -n 20 "${WECHAT_LOG_FILE}" || true
53
+ fi
@@ -1,6 +1,6 @@
1
- #!/bin/zsh
2
- set -euo pipefail
3
-
1
+ #!/bin/zsh
2
+ set -euo pipefail
3
+
4
4
  PORT="${CODEKSEI_SHARED_PORT:-${CYBERBOSS_SHARED_PORT:-8765}}"
5
5
  LISTEN_URL="ws://127.0.0.1:${PORT}"
6
6
  STATE_DIR="${CODEKSEI_STATE_DIR:-${CYBERBOSS_STATE_DIR:-$HOME/.codeksei}}"
@@ -8,58 +8,58 @@ if [[ ! -d "${STATE_DIR}" && -d "$HOME/.cyberboss" ]]; then
8
8
  STATE_DIR="$HOME/.cyberboss"
9
9
  fi
10
10
  LOG_DIR="${STATE_DIR}/logs"
11
- PID_FILE="${LOG_DIR}/shared-app-server.pid"
12
- LOG_FILE="${LOG_DIR}/shared-app-server.log"
13
-
14
- function lookup_listen_pid() {
15
- lsof -nP -iTCP:"${PORT}" -sTCP:LISTEN 2>/dev/null \
16
- | awk 'NR > 1 { print $2; found=1; exit } END { if (!found) exit 0 }'
17
- }
18
-
19
- mkdir -p "${LOG_DIR}"
11
+ PID_FILE="${LOG_DIR}/shared-app-server.pid"
12
+ LOG_FILE="${LOG_DIR}/shared-app-server.log"
13
+
14
+ function lookup_listen_pid() {
15
+ lsof -nP -iTCP:"${PORT}" -sTCP:LISTEN 2>/dev/null \
16
+ | awk 'NR > 1 { print $2; found=1; exit } END { if (!found) exit 0 }'
17
+ }
18
+
19
+ mkdir -p "${LOG_DIR}"
20
20
  export CODEKSEI_STATE_DIR="${STATE_DIR}"
21
21
  export CYBERBOSS_STATE_DIR="${STATE_DIR}"
22
22
  export TIMELINE_FOR_AGENT_STATE_DIR="${STATE_DIR}"
23
23
  if [[ -z "${TIMELINE_FOR_AGENT_CHROME_PATH:-}" ]]; then
24
24
  export TIMELINE_FOR_AGENT_CHROME_PATH="${CODEKSEI_SCREENSHOT_CHROME_PATH:-${CYBERBOSS_SCREENSHOT_CHROME_PATH:-/Applications/Google Chrome.app/Contents/MacOS/Google Chrome}}"
25
25
  fi
26
-
27
- if [[ -f "${PID_FILE}" ]]; then
28
- EXISTING_PID="$(cat "${PID_FILE}")"
29
- if [[ -n "${EXISTING_PID}" ]] && kill -0 "${EXISTING_PID}" 2>/dev/null; then
30
- echo "shared app-server already running pid=${EXISTING_PID} listen=${LISTEN_URL}"
31
- exit 0
32
- fi
33
- rm -f "${PID_FILE}"
34
- fi
35
-
36
- EXISTING_PID="$(lookup_listen_pid || true)"
37
- if [[ -n "${EXISTING_PID}" ]]; then
38
- echo "${EXISTING_PID}" > "${PID_FILE}"
39
- echo "shared app-server already running pid=${EXISTING_PID} listen=${LISTEN_URL}"
40
- exit 0
41
- fi
42
-
43
- nohup codex app-server --listen "${LISTEN_URL}" >> "${LOG_FILE}" 2>&1 &
44
- APP_SERVER_PID=$!
45
- echo "${APP_SERVER_PID}" > "${PID_FILE}"
46
- sleep 1
47
-
48
- LISTEN_PID="$(lookup_listen_pid || true)"
49
- if kill -0 "${APP_SERVER_PID}" 2>/dev/null && [[ -n "${LISTEN_PID}" ]]; then
50
- echo "${LISTEN_PID}" > "${PID_FILE}"
51
- echo "started shared app-server pid=${LISTEN_PID} listen=${LISTEN_URL}"
52
- echo "log=${LOG_FILE}"
53
- exit 0
54
- fi
55
-
56
- EXISTING_PID="$(lookup_listen_pid || true)"
57
- if [[ -n "${EXISTING_PID}" ]]; then
58
- echo "${EXISTING_PID}" > "${PID_FILE}"
59
- echo "shared app-server already running pid=${EXISTING_PID} listen=${LISTEN_URL}"
60
- exit 0
61
- fi
62
-
63
- echo "failed to start shared app-server; check ${LOG_FILE}" >&2
64
- tail -n 20 "${LOG_FILE}" >&2 || true
65
- exit 1
26
+
27
+ if [[ -f "${PID_FILE}" ]]; then
28
+ EXISTING_PID="$(cat "${PID_FILE}")"
29
+ if [[ -n "${EXISTING_PID}" ]] && kill -0 "${EXISTING_PID}" 2>/dev/null; then
30
+ echo "shared app-server already running pid=${EXISTING_PID} listen=${LISTEN_URL}"
31
+ exit 0
32
+ fi
33
+ rm -f "${PID_FILE}"
34
+ fi
35
+
36
+ EXISTING_PID="$(lookup_listen_pid || true)"
37
+ if [[ -n "${EXISTING_PID}" ]]; then
38
+ echo "${EXISTING_PID}" > "${PID_FILE}"
39
+ echo "shared app-server already running pid=${EXISTING_PID} listen=${LISTEN_URL}"
40
+ exit 0
41
+ fi
42
+
43
+ nohup codex app-server --listen "${LISTEN_URL}" >> "${LOG_FILE}" 2>&1 &
44
+ APP_SERVER_PID=$!
45
+ echo "${APP_SERVER_PID}" > "${PID_FILE}"
46
+ sleep 1
47
+
48
+ LISTEN_PID="$(lookup_listen_pid || true)"
49
+ if kill -0 "${APP_SERVER_PID}" 2>/dev/null && [[ -n "${LISTEN_PID}" ]]; then
50
+ echo "${LISTEN_PID}" > "${PID_FILE}"
51
+ echo "started shared app-server pid=${LISTEN_PID} listen=${LISTEN_URL}"
52
+ echo "log=${LOG_FILE}"
53
+ exit 0
54
+ fi
55
+
56
+ EXISTING_PID="$(lookup_listen_pid || true)"
57
+ if [[ -n "${EXISTING_PID}" ]]; then
58
+ echo "${EXISTING_PID}" > "${PID_FILE}"
59
+ echo "shared app-server already running pid=${EXISTING_PID} listen=${LISTEN_URL}"
60
+ exit 0
61
+ fi
62
+
63
+ echo "failed to start shared app-server; check ${LOG_FILE}" >&2
64
+ tail -n 20 "${LOG_FILE}" >&2 || true
65
+ exit 1