codeksei 0.1.0

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 (80) hide show
  1. package/LICENSE +661 -0
  2. package/README.en.md +215 -0
  3. package/README.md +259 -0
  4. package/bin/codeksei.js +10 -0
  5. package/bin/cyberboss.js +11 -0
  6. package/package.json +86 -0
  7. package/scripts/install-background-tasks.ps1 +135 -0
  8. package/scripts/open_shared_wechat_thread.sh +94 -0
  9. package/scripts/open_wechat_thread.sh +117 -0
  10. package/scripts/shared-common.js +791 -0
  11. package/scripts/shared-open.js +46 -0
  12. package/scripts/shared-start.js +41 -0
  13. package/scripts/shared-status.js +74 -0
  14. package/scripts/shared-supervisor.js +141 -0
  15. package/scripts/shared-task-runner.ps1 +87 -0
  16. package/scripts/shared-watchdog.js +290 -0
  17. package/scripts/show_shared_status.sh +53 -0
  18. package/scripts/start_shared_app_server.sh +65 -0
  19. package/scripts/start_shared_wechat.sh +108 -0
  20. package/scripts/timeline-screenshot.sh +15 -0
  21. package/scripts/uninstall-background-tasks.ps1 +23 -0
  22. package/src/adapters/channel/weixin/account-store.js +135 -0
  23. package/src/adapters/channel/weixin/api-v2.js +258 -0
  24. package/src/adapters/channel/weixin/api.js +180 -0
  25. package/src/adapters/channel/weixin/context-token-store.js +84 -0
  26. package/src/adapters/channel/weixin/index.js +605 -0
  27. package/src/adapters/channel/weixin/legacy.js +567 -0
  28. package/src/adapters/channel/weixin/login-common.js +63 -0
  29. package/src/adapters/channel/weixin/login-legacy.js +124 -0
  30. package/src/adapters/channel/weixin/login-v2.js +186 -0
  31. package/src/adapters/channel/weixin/media-mime.js +22 -0
  32. package/src/adapters/channel/weixin/media-receive.js +370 -0
  33. package/src/adapters/channel/weixin/media-send.js +331 -0
  34. package/src/adapters/channel/weixin/message-utils-v2.js +282 -0
  35. package/src/adapters/channel/weixin/message-utils.js +199 -0
  36. package/src/adapters/channel/weixin/protocol.js +77 -0
  37. package/src/adapters/channel/weixin/redact.js +41 -0
  38. package/src/adapters/channel/weixin/reminder-queue-store.js +101 -0
  39. package/src/adapters/channel/weixin/sync-buffer-store.js +35 -0
  40. package/src/adapters/runtime/codex/events.js +252 -0
  41. package/src/adapters/runtime/codex/index.js +502 -0
  42. package/src/adapters/runtime/codex/message-utils.js +141 -0
  43. package/src/adapters/runtime/codex/model-catalog.js +106 -0
  44. package/src/adapters/runtime/codex/protocol-leak-monitor.js +75 -0
  45. package/src/adapters/runtime/codex/rpc-client.js +443 -0
  46. package/src/adapters/runtime/codex/session-store.js +376 -0
  47. package/src/app/channel-send-file-cli.js +57 -0
  48. package/src/app/diary-write-cli.js +620 -0
  49. package/src/app/note-auto-cli.js +201 -0
  50. package/src/app/note-sync-cli.js +130 -0
  51. package/src/app/project-radar-cli.js +165 -0
  52. package/src/app/reminder-write-cli.js +210 -0
  53. package/src/app/review-cli.js +134 -0
  54. package/src/app/system-checkin-poller.js +100 -0
  55. package/src/app/system-send-cli.js +129 -0
  56. package/src/app/timeline-event-cli.js +273 -0
  57. package/src/app/timeline-screenshot-cli.js +109 -0
  58. package/src/core/app.js +1810 -0
  59. package/src/core/branding.js +167 -0
  60. package/src/core/command-registry.js +609 -0
  61. package/src/core/config.js +84 -0
  62. package/src/core/default-targets.js +163 -0
  63. package/src/core/durable-note-schema.js +325 -0
  64. package/src/core/instructions-template.js +31 -0
  65. package/src/core/note-sync.js +433 -0
  66. package/src/core/project-radar.js +402 -0
  67. package/src/core/review-semantic.js +524 -0
  68. package/src/core/review.js +1081 -0
  69. package/src/core/shared-bridge-heartbeat.js +140 -0
  70. package/src/core/stream-delivery.js +990 -0
  71. package/src/core/system-message-dispatcher.js +68 -0
  72. package/src/core/system-message-queue-store.js +128 -0
  73. package/src/core/thread-state-store.js +135 -0
  74. package/src/core/timeline-screenshot-queue-store.js +134 -0
  75. package/src/core/workspace-alias.js +163 -0
  76. package/src/core/workspace-bootstrap.js +338 -0
  77. package/src/index.js +270 -0
  78. package/src/integrations/timeline/index.js +191 -0
  79. package/templates/weixin-instructions.md +53 -0
  80. package/templates/weixin-operations.md +69 -0
@@ -0,0 +1,135 @@
1
+ param(
2
+ [int]$WatchdogMinutes = 5
3
+ )
4
+
5
+ $ErrorActionPreference = "Stop"
6
+
7
+ if ($WatchdogMinutes -lt 1) {
8
+ throw "WatchdogMinutes 必须大于等于 1"
9
+ }
10
+
11
+ $runnerPath = Join-Path $PSScriptRoot "shared-task-runner.ps1"
12
+ $currentUser = [System.Security.Principal.WindowsIdentity]::GetCurrent().Name
13
+ $taskNames = @{
14
+ start = "Codeksei Shared Start"
15
+ unlock = "Codeksei Shared Unlock"
16
+ resume = "Codeksei Shared Resume"
17
+ watchdog = "Codeksei Shared Watchdog"
18
+ }
19
+ $legacyTaskNames = @(
20
+ "Cyberboss Shared Start",
21
+ "Cyberboss Shared Unlock",
22
+ "Cyberboss Shared Resume",
23
+ "Cyberboss Shared Watchdog"
24
+ )
25
+
26
+ function Escape-XmlText {
27
+ param([string]$Value)
28
+ [System.Security.SecurityElement]::Escape($Value)
29
+ }
30
+
31
+ function Register-CyberbossTaskXml {
32
+ param(
33
+ [string]$TaskName,
34
+ [string]$TriggerXml,
35
+ [string]$ArgumentsXml
36
+ )
37
+
38
+ $escapedUser = Escape-XmlText $currentUser
39
+ $taskXml = @"
40
+ <?xml version="1.0" encoding="UTF-16"?>
41
+ <Task version="1.3" xmlns="http://schemas.microsoft.com/windows/2004/02/mit/task">
42
+ <RegistrationInfo>
43
+ <URI>\$TaskName</URI>
44
+ <SecurityDescriptor></SecurityDescriptor>
45
+ </RegistrationInfo>
46
+ <Triggers>
47
+ $TriggerXml
48
+ </Triggers>
49
+ <Principals>
50
+ <Principal id="Author">
51
+ <UserId>$escapedUser</UserId>
52
+ <LogonType>InteractiveToken</LogonType>
53
+ <RunLevel>LeastPrivilege</RunLevel>
54
+ </Principal>
55
+ </Principals>
56
+ <Settings>
57
+ <MultipleInstancesPolicy>IgnoreNew</MultipleInstancesPolicy>
58
+ <DisallowStartIfOnBatteries>false</DisallowStartIfOnBatteries>
59
+ <StopIfGoingOnBatteries>false</StopIfGoingOnBatteries>
60
+ <AllowHardTerminate>true</AllowHardTerminate>
61
+ <StartWhenAvailable>true</StartWhenAvailable>
62
+ <RunOnlyIfNetworkAvailable>false</RunOnlyIfNetworkAvailable>
63
+ <IdleSettings>
64
+ <Duration>PT10M</Duration>
65
+ <WaitTimeout>PT1H</WaitTimeout>
66
+ <StopOnIdleEnd>true</StopOnIdleEnd>
67
+ <RestartOnIdle>false</RestartOnIdle>
68
+ </IdleSettings>
69
+ <AllowStartOnDemand>true</AllowStartOnDemand>
70
+ <Enabled>true</Enabled>
71
+ <Hidden>false</Hidden>
72
+ <RunOnlyIfIdle>false</RunOnlyIfIdle>
73
+ <DisallowStartOnRemoteAppSession>false</DisallowStartOnRemoteAppSession>
74
+ <UseUnifiedSchedulingEngine>true</UseUnifiedSchedulingEngine>
75
+ <WakeToRun>false</WakeToRun>
76
+ <ExecutionTimeLimit>PT10M</ExecutionTimeLimit>
77
+ <Priority>7</Priority>
78
+ </Settings>
79
+ <Actions Context="Author">
80
+ <Exec>
81
+ <Command>powershell.exe</Command>
82
+ <Arguments>$ArgumentsXml</Arguments>
83
+ </Exec>
84
+ </Actions>
85
+ </Task>
86
+ "@
87
+
88
+ Register-ScheduledTask -TaskName $TaskName -Xml $taskXml -Force | Out-Null
89
+ }
90
+
91
+ foreach ($taskName in @($taskNames.start, $taskNames.unlock, $taskNames.resume, $taskNames.watchdog) + $legacyTaskNames) {
92
+ try {
93
+ Unregister-ScheduledTask -TaskName $taskName -Confirm:$false -ErrorAction Stop | Out-Null
94
+ } catch {
95
+ # best effort; task may not exist yet
96
+ }
97
+ }
98
+
99
+ $bootstrapArguments = Escape-XmlText "-NoProfile -ExecutionPolicy Bypass -WindowStyle Hidden -File `"$runnerPath`" -Mode Start -IntervalMinutes $WatchdogMinutes"
100
+ $escapedUser = Escape-XmlText $currentUser
101
+ $logonTriggerXml = @"
102
+ <LogonTrigger>
103
+ <Enabled>true</Enabled>
104
+ <UserId>$escapedUser</UserId>
105
+ </LogonTrigger>
106
+ "@
107
+ $unlockTriggerXml = @"
108
+ <SessionStateChangeTrigger>
109
+ <Enabled>true</Enabled>
110
+ <StateChange>SessionUnlock</StateChange>
111
+ <UserId>$escapedUser</UserId>
112
+ </SessionStateChangeTrigger>
113
+ "@
114
+ $resumeTriggerXml = @"
115
+ <EventTrigger>
116
+ <Enabled>true</Enabled>
117
+ <Subscription><![CDATA[<QueryList><Query Id="0" Path="System"><Select Path="System">*[System[Provider[@Name='Microsoft-Windows-Power-Troubleshooter'] and EventID=1]]</Select></Query></QueryList>]]></Subscription>
118
+ </EventTrigger>
119
+ "@
120
+
121
+ # Use short-lived event-driven bootstrap tasks instead of a periodic task.
122
+ # The detached supervisor keeps idle checks quiet, while logon/unlock/resume
123
+ # pokes recover quickly after session lifecycle changes without relaunching a
124
+ # watchdog shell every few minutes.
125
+ Register-CyberbossTaskXml -TaskName $taskNames.start -TriggerXml $logonTriggerXml -ArgumentsXml $bootstrapArguments
126
+ Register-CyberbossTaskXml -TaskName $taskNames.unlock -TriggerXml $unlockTriggerXml -ArgumentsXml $bootstrapArguments
127
+ Register-CyberbossTaskXml -TaskName $taskNames.resume -TriggerXml $resumeTriggerXml -ArgumentsXml $bootstrapArguments
128
+
129
+ Start-ScheduledTask -TaskName $taskNames.start
130
+
131
+ Write-Host "installed=$($taskNames.start)"
132
+ Write-Host "installed=$($taskNames.unlock)"
133
+ Write-Host "installed=$($taskNames.resume)"
134
+ Write-Host "removed=$($taskNames.watchdog)"
135
+ Write-Host "supervisor_interval_minutes=$WatchdogMinutes"
@@ -0,0 +1,94 @@
1
+ #!/bin/zsh
2
+ set -euo pipefail
3
+
4
+ ROOT_DIR="$(cd "$(dirname "$0")/.." && pwd)"
5
+ PORT="${CODEKSEI_SHARED_PORT:-${CYBERBOSS_SHARED_PORT:-8765}}"
6
+ REMOTE_URL="ws://127.0.0.1:${PORT}"
7
+ STATE_DIR="${CODEKSEI_STATE_DIR:-${CYBERBOSS_STATE_DIR:-$HOME/.codeksei}}"
8
+ if [[ ! -d "${STATE_DIR}" && -d "$HOME/.cyberboss" ]]; then
9
+ STATE_DIR="$HOME/.cyberboss"
10
+ fi
11
+ LOG_DIR="${STATE_DIR}/logs"
12
+ PID_FILE="${LOG_DIR}/shared-wechat.pid"
13
+
14
+ mkdir -p "${LOG_DIR}"
15
+
16
+ function resolve_pid_cwd() {
17
+ local pid="$1"
18
+ lsof -a -p "${pid}" -d cwd -Fn 2>/dev/null | sed -n 's/^n//p' | head -n 1
19
+ }
20
+
21
+ function list_bridge_processes() {
22
+ ps -ax -o pid=,ppid=,command= | awk '/node \.\/bin\/(codeksei|cyberboss)\.js start --checkin/ { print }'
23
+ }
24
+
25
+ function find_bridge_child_pid() {
26
+ local parent_pid="$1"
27
+ list_bridge_processes | awk -v target_ppid="${parent_pid}" '$2 == target_ppid { print $1; exit }'
28
+ }
29
+
30
+ function resolve_bridge_pid() {
31
+ local candidate_pid="$1"
32
+ [[ -n "${candidate_pid}" ]] || return 1
33
+ if ! kill -0 "${candidate_pid}" 2>/dev/null; then
34
+ return 1
35
+ fi
36
+
37
+ local child_pid
38
+ child_pid="$(find_bridge_child_pid "${candidate_pid}")"
39
+ if [[ -n "${child_pid}" ]]; then
40
+ echo "${child_pid}"
41
+ return 0
42
+ fi
43
+
44
+ if [[ "$(resolve_pid_cwd "${candidate_pid}")" == "${ROOT_DIR}" ]]; then
45
+ echo "${candidate_pid}"
46
+ return 0
47
+ fi
48
+
49
+ return 1
50
+ }
51
+
52
+ function find_existing_bridge_pid() {
53
+ if [[ -f "${PID_FILE}" ]]; then
54
+ local pid_from_file
55
+ pid_from_file="$(cat "${PID_FILE}" 2>/dev/null || true)"
56
+ local resolved_from_file
57
+ resolved_from_file="$(resolve_bridge_pid "${pid_from_file}" || true)"
58
+ if [[ -n "${resolved_from_file}" ]]; then
59
+ echo "${resolved_from_file}"
60
+ return 0
61
+ fi
62
+ fi
63
+
64
+ local pid
65
+ while read -r pid _; do
66
+ [[ -n "${pid}" ]] || continue
67
+ if [[ "$(resolve_pid_cwd "${pid}")" == "${ROOT_DIR}" ]]; then
68
+ echo "${pid}"
69
+ return 0
70
+ fi
71
+ done < <(list_bridge_processes)
72
+
73
+ return 1
74
+ }
75
+
76
+ "${ROOT_DIR}/scripts/start_shared_app_server.sh"
77
+
78
+ EXISTING_PID="$(find_existing_bridge_pid || true)"
79
+
80
+ if [[ -z "${EXISTING_PID}" ]]; then
81
+ echo "shared codeksei is not running." >&2
82
+ echo "start it in a separate terminal and keep it in the foreground:" >&2
83
+ echo " cd ${ROOT_DIR}" >&2
84
+ echo " ./scripts/start_shared_wechat.sh" >&2
85
+ exit 1
86
+ fi
87
+
88
+ echo "${EXISTING_PID}" > "${PID_FILE}"
89
+
90
+ echo "shared codeksei running pid=${EXISTING_PID} endpoint=${REMOTE_URL}"
91
+
92
+ export CODEKSEI_CODEX_ENDPOINT="${REMOTE_URL}"
93
+ export CYBERBOSS_CODEX_ENDPOINT="${REMOTE_URL}"
94
+ exec "${ROOT_DIR}/scripts/open_wechat_thread.sh" "$@"
@@ -0,0 +1,117 @@
1
+ #!/bin/zsh
2
+ set -euo pipefail
3
+
4
+ PORT="${CODEKSEI_SHARED_PORT:-${CYBERBOSS_SHARED_PORT:-8765}}"
5
+ REMOTE_URL="${CODEKSEI_CODEX_ENDPOINT:-${CYBERBOSS_CODEX_ENDPOINT:-ws://127.0.0.1:${PORT}}}"
6
+ STATE_DIR="${CODEKSEI_STATE_DIR:-${CYBERBOSS_STATE_DIR:-$HOME/.codeksei}}"
7
+ if [[ ! -d "${STATE_DIR}" && -d "$HOME/.cyberboss" ]]; then
8
+ STATE_DIR="$HOME/.cyberboss"
9
+ fi
10
+ SESSION_FILE="${CODEKSEI_SESSIONS_FILE:-${CYBERBOSS_SESSIONS_FILE:-${STATE_DIR}/sessions.json}}"
11
+ WORKSPACE_ROOT="${CODEKSEI_WORKSPACE_ROOT:-${CYBERBOSS_WORKSPACE_ROOT:-$PWD}}"
12
+ ACCOUNT_DIR="${STATE_DIR}/accounts"
13
+
14
+ if [[ ! -f "${SESSION_FILE}" ]]; then
15
+ echo "session file not found: ${SESSION_FILE}" >&2
16
+ exit 1
17
+ fi
18
+
19
+ RESOLVED="$(
20
+ node -e '
21
+ const fs = require("fs");
22
+ const path = require("path");
23
+
24
+ const sessionFile = process.argv[1];
25
+ const workspaceRoot = process.argv[2];
26
+ const accountDir = process.argv[3];
27
+ const data = JSON.parse(fs.readFileSync(sessionFile, "utf8"));
28
+ const bindings = Object.entries(data.bindings || {}).map(([bindingKey, binding]) => ({ bindingKey, ...(binding || {}) }));
29
+
30
+ function normalize(value) {
31
+ return typeof value === "string" ? value.trim() : "";
32
+ }
33
+
34
+ function toTimestamp(value) {
35
+ const parsed = Date.parse(normalize(value));
36
+ return Number.isFinite(parsed) ? parsed : 0;
37
+ }
38
+
39
+ function resolveCurrentAccountId(dir) {
40
+ const normalizedDir = normalize(dir);
41
+ if (!normalizedDir || !fs.existsSync(normalizedDir)) {
42
+ return "";
43
+ }
44
+
45
+ const entries = fs.readdirSync(normalizedDir)
46
+ .filter((name) => name.endsWith(".json") && !name.endsWith(".context-tokens.json"))
47
+ .map((name) => {
48
+ const fullPath = path.join(normalizedDir, name);
49
+ try {
50
+ const parsed = JSON.parse(fs.readFileSync(fullPath, "utf8"));
51
+ return {
52
+ accountId: normalize(parsed && parsed.accountId),
53
+ savedAt: toTimestamp(parsed && parsed.savedAt),
54
+ };
55
+ } catch {
56
+ return null;
57
+ }
58
+ })
59
+ .filter(Boolean)
60
+ .filter((entry) => entry.accountId);
61
+
62
+ entries.sort((left, right) => right.savedAt - left.savedAt);
63
+ return entries[0]?.accountId || "";
64
+ }
65
+
66
+ function getThreadId(binding, root) {
67
+ const normalizedRoot = normalize(root);
68
+ if (!normalizedRoot) {
69
+ return "";
70
+ }
71
+ const map = binding && typeof binding.threadIdByWorkspaceRoot === "object"
72
+ ? binding.threadIdByWorkspaceRoot
73
+ : {};
74
+ return normalize(map[normalizedRoot]);
75
+ }
76
+
77
+ const normalizedWorkspaceRoot = normalize(workspaceRoot);
78
+ const currentAccountId = resolveCurrentAccountId(accountDir);
79
+
80
+ const filteredBindings = bindings
81
+ .filter((binding) => !currentAccountId || normalize(binding.accountId) === currentAccountId)
82
+ .sort((left, right) => toTimestamp(right.updatedAt) - toTimestamp(left.updatedAt));
83
+
84
+ const exactBinding = filteredBindings.find((binding) => getThreadId(binding, normalizedWorkspaceRoot));
85
+ if (exactBinding) {
86
+ process.stdout.write(`${getThreadId(exactBinding, normalizedWorkspaceRoot)}\n${normalizedWorkspaceRoot}`);
87
+ process.exit(0);
88
+ }
89
+
90
+ const activeBinding = filteredBindings.find((binding) => {
91
+ const activeWorkspaceRoot = normalize(binding && binding.activeWorkspaceRoot);
92
+ return activeWorkspaceRoot && getThreadId(binding, activeWorkspaceRoot);
93
+ });
94
+ if (activeBinding) {
95
+ const activeWorkspaceRoot = normalize(activeBinding.activeWorkspaceRoot);
96
+ process.stdout.write(`${getThreadId(activeBinding, activeWorkspaceRoot)}\n${activeWorkspaceRoot}`);
97
+ process.exit(0);
98
+ }
99
+
100
+ process.exit(1);
101
+ ' "${SESSION_FILE}" "${WORKSPACE_ROOT}" "${ACCOUNT_DIR}"
102
+ )"
103
+
104
+ if [[ -z "${RESOLVED}" ]]; then
105
+ echo "no bound WeChat thread found for workspace: ${WORKSPACE_ROOT}" >&2
106
+ exit 1
107
+ fi
108
+
109
+ THREAD_ID="${RESOLVED%%$'\n'*}"
110
+ RESOLVED_WORKSPACE_ROOT="${RESOLVED#*$'\n'}"
111
+
112
+ if [[ -z "${THREAD_ID}" || -z "${RESOLVED_WORKSPACE_ROOT}" ]]; then
113
+ echo "failed to resolve bound WeChat thread from: ${SESSION_FILE}" >&2
114
+ exit 1
115
+ fi
116
+
117
+ exec codex resume "${THREAD_ID}" --remote "${REMOTE_URL}" -C "${RESOLVED_WORKSPACE_ROOT}" "$@"