work-ally 0.2.0-alpha.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 (172) hide show
  1. package/AGENTS.md +110 -0
  2. package/DASHBOARD.md +160 -0
  3. package/PRODUCT.md +113 -0
  4. package/README.md +403 -0
  5. package/ally.sh +171 -0
  6. package/bridge/src/approval-rules.ts +360 -0
  7. package/bridge/src/channel-delivery.ts +207 -0
  8. package/bridge/src/channel-types.ts +22 -0
  9. package/bridge/src/channels/fake/adapter.ts +31 -0
  10. package/bridge/src/channels/feishu/adapter.ts +411 -0
  11. package/bridge/src/channels/feishu/approvals.ts +6 -0
  12. package/bridge/src/channels/feishu/formatter.ts +276 -0
  13. package/bridge/src/channels/feishu/normalize.ts +368 -0
  14. package/bridge/src/codex-config.ts +52 -0
  15. package/bridge/src/config.ts +240 -0
  16. package/bridge/src/fake-runtime-client.ts +505 -0
  17. package/bridge/src/handoff-service.ts +494 -0
  18. package/bridge/src/logger.ts +194 -0
  19. package/bridge/src/memory-digest.ts +186 -0
  20. package/bridge/src/receiver-approval-autonomy.ts +158 -0
  21. package/bridge/src/receiver-control-core.ts +140 -0
  22. package/bridge/src/receiver-control-work-session.ts +218 -0
  23. package/bridge/src/receiver-control.ts +83 -0
  24. package/bridge/src/receiver-delivery.ts +136 -0
  25. package/bridge/src/receiver-helpers.ts +96 -0
  26. package/bridge/src/receiver-human-gate.ts +333 -0
  27. package/bridge/src/receiver-inbound-preflight.ts +162 -0
  28. package/bridge/src/receiver-recovery.ts +236 -0
  29. package/bridge/src/receiver-runtime-callbacks.ts +367 -0
  30. package/bridge/src/receiver-runtime-policy.ts +132 -0
  31. package/bridge/src/receiver-runtime-state.ts +124 -0
  32. package/bridge/src/receiver-support-actions.ts +189 -0
  33. package/bridge/src/receiver-thread-start.ts +57 -0
  34. package/bridge/src/receiver-turn-coordination.ts +94 -0
  35. package/bridge/src/receiver-turn-execution.ts +257 -0
  36. package/bridge/src/receiver-turn-failure.ts +143 -0
  37. package/bridge/src/receiver-turn-result.ts +185 -0
  38. package/bridge/src/receiver-turn-steer.ts +70 -0
  39. package/bridge/src/receiver-work-session.ts +76 -0
  40. package/bridge/src/receiver.ts +329 -0
  41. package/bridge/src/router.ts +62 -0
  42. package/bridge/src/runtime-client-agent-messages.ts +150 -0
  43. package/bridge/src/runtime-client-message-dispatch.ts +176 -0
  44. package/bridge/src/runtime-client-protocol.ts +411 -0
  45. package/bridge/src/runtime-client-request-ops.ts +56 -0
  46. package/bridge/src/runtime-client-run-turn.ts +158 -0
  47. package/bridge/src/runtime-client-thread-ops.ts +270 -0
  48. package/bridge/src/runtime-client-transport.ts +309 -0
  49. package/bridge/src/runtime-client-turn-poll.ts +224 -0
  50. package/bridge/src/runtime-client-turn-read.ts +185 -0
  51. package/bridge/src/runtime-client-turn-state.ts +105 -0
  52. package/bridge/src/runtime-client.ts +344 -0
  53. package/bridge/src/runtime-user-input.ts +403 -0
  54. package/bridge/src/scheduler.ts +239 -0
  55. package/bridge/src/server-handoff-command.ts +364 -0
  56. package/bridge/src/server-main.ts +80 -0
  57. package/bridge/src/server-routine-command.ts +60 -0
  58. package/bridge/src/server-routine-execution.ts +222 -0
  59. package/bridge/src/server-runtime-app-support.ts +107 -0
  60. package/bridge/src/server-runtime-app.ts +238 -0
  61. package/bridge/src/server-thread-sync-command.ts +63 -0
  62. package/bridge/src/server.ts +17 -0
  63. package/bridge/src/session-store-delivery.ts +220 -0
  64. package/bridge/src/session-store-human-gate.ts +380 -0
  65. package/bridge/src/session-store-inbound-acceptance.ts +66 -0
  66. package/bridge/src/session-store-meta.ts +134 -0
  67. package/bridge/src/session-store-turn-ledger.ts +272 -0
  68. package/bridge/src/session-store.ts +380 -0
  69. package/bridge/src/system-notify.ts +220 -0
  70. package/bridge/src/thread-sync.ts +200 -0
  71. package/bridge/src/translator.ts +494 -0
  72. package/bridge/src/types.ts +289 -0
  73. package/bridge/src/utils.ts +104 -0
  74. package/bridge/src/work-session-store.ts +471 -0
  75. package/docs/.gitkeep +0 -0
  76. package/docs/architecture/codex-feishu-bridge-proposal.md +2742 -0
  77. package/docs/completed/FEATURE-feishu-markdown-and-reply-support.md +327 -0
  78. package/docs/completed/README.md +21 -0
  79. package/docs/completed/SPEC-approval-autonomy-and-safe-defaults.md +205 -0
  80. package/docs/completed/SPEC-approval-batch-and-strict-reply-shortcuts.md +153 -0
  81. package/docs/completed/SPEC-conversation-noise-reduction-and-busy-input-gate.md +538 -0
  82. package/docs/completed/SPEC-engineering-sop-skillization.md +190 -0
  83. package/docs/completed/SPEC-faithful-bridge-core-thinning-v2.md +376 -0
  84. package/docs/completed/SPEC-faithful-bridge-core-thinning.md +1071 -0
  85. package/docs/completed/SPEC-group-chat-sender-identity.md +301 -0
  86. package/docs/completed/SPEC-middleware-exception-visibility.md +227 -0
  87. package/docs/completed/SPEC-nightly-memory-digest-visibility.md +121 -0
  88. package/docs/completed/SPEC-project-group-chat-human-centered-conversation-mapping.md +326 -0
  89. package/docs/completed/SPEC-remove-cli-persona-bootstrap.md +201 -0
  90. package/docs/developer-workflow.md +49 -0
  91. package/docs/implementation/SPEC-codex-same-machine-session-handoff-implementation.md +239 -0
  92. package/docs/implementation/test-coverage-map.md +363 -0
  93. package/docs/implementation/work-ally-implementation-guide.md +790 -0
  94. package/docs/issues/README.md +10 -0
  95. package/docs/issues/pending/ANALYSIS-ally-premature-recovery-notice-and-task-state-semantics-2026-03-18.md +295 -0
  96. package/docs/issues/resolved/ANALYSIS-approval-waiting-visible-but-approval-artifact-missing-2026-03-16.md +466 -0
  97. package/docs/issues/resolved/ANALYSIS-blocking-state-visible-without-user-actionable-artifact-2026-03-16.md +261 -0
  98. package/docs/issues/resolved/ANALYSIS-codex-app-server-transport-disconnect-semantics-2026-03-14.md +606 -0
  99. package/docs/issues/resolved/ANALYSIS-premature-terminalization-on-fresh-thread-poll-and-object-error-leak-2026-03-16.md +348 -0
  100. package/docs/issues/resolved/ANALYSIS-runtime-turn-delivery-and-recovery-2026-03-14.md +603 -0
  101. package/docs/issues/resolved/ANALYSIS-self-test-gap-approval-waiting-visible-but-approval-artifact-missing-2026-03-16.md +166 -0
  102. package/docs/issues/resolved/ANALYSIS-self-test-gap-blocking-state-visible-without-user-actionable-artifact-2026-03-16.md +186 -0
  103. package/docs/issues/resolved/ANALYSIS-self-test-gap-premature-terminalization-on-fresh-thread-poll-and-object-error-leak-2026-03-16.md +166 -0
  104. package/docs/issues/resolved/REPORT-ally-runtime-turn-delivery-3b42fb8-2026-03-15.md +373 -0
  105. package/docs/manual-acceptance.md +127 -0
  106. package/docs/ops-runbook.md +44 -0
  107. package/docs/planning/FEATURE-memory-system.md +748 -0
  108. package/docs/planning/SPEC-active-turn-steer-and-context-compaction-visibility.md +269 -0
  109. package/docs/planning/SPEC-approval-rules-inheritance-and-local-validation-lane.md +450 -0
  110. package/docs/planning/SPEC-assistant-persona-bootstrap.md +199 -0
  111. package/docs/planning/SPEC-assistant-rename.md +610 -0
  112. package/docs/planning/SPEC-bridge-app-server-protocol-alignment.md +667 -0
  113. package/docs/planning/SPEC-claude-runtime-host-for-work-ally.md +434 -0
  114. package/docs/planning/SPEC-cli-feishu-codex-session-unification.md +236 -0
  115. package/docs/planning/SPEC-codex-same-machine-session-handoff.md +873 -0
  116. package/docs/planning/SPEC-feishu-reaction-shortcuts.md +282 -0
  117. package/docs/planning/SPEC-local-stable-release-boundary.md +166 -0
  118. package/docs/planning/SPEC-managed-thread-entry-and-surface-mobility.md +862 -0
  119. package/docs/planning/SPEC-minimal-bridge-semantics-and-user-visible-surface.md +362 -0
  120. package/docs/planning/SPEC-npm-alpha-distribution-and-install-first-release.md +222 -0
  121. package/docs/planning/SPEC-remove-websocket-runtime-transport.md +364 -0
  122. package/docs/planning/SPEC-runtime-abstraction-phase-1.md +424 -0
  123. package/docs/planning/SPEC-runtime-connection-and-turn-recovery-semantics.md +274 -0
  124. package/docs/planning/SPEC-session-presence-and-state-visibility.md +397 -0
  125. package/docs/planning/SPEC-skill-first-capability-packaging.md +338 -0
  126. package/docs/planning/SPEC-stable-archive-contract.md +456 -0
  127. package/docs/planning/SPEC-supervised-start-boundary.md +127 -0
  128. package/docs/planning/SPEC-user-barrier-reduction-and-activation.md +832 -0
  129. package/docs/planning/ally-next.md +1278 -0
  130. package/docs/planning/assistant-workbench-spec.md +725 -0
  131. package/docs/planning/product-workbench.md +283 -0
  132. package/docs/product-onboarding.md +227 -0
  133. package/docs/product-spec-standard.md +528 -0
  134. package/docs/troubleshooting.md +45 -0
  135. package/docs/user-quickstart.md +46 -0
  136. package/internal/dispatch.sh +95 -0
  137. package/internal/lib/common.sh +1450 -0
  138. package/internal/modules/assistant/manage.sh +1312 -0
  139. package/internal/modules/bootstrap/setup.sh +144 -0
  140. package/internal/modules/config/init-env.sh +10 -0
  141. package/internal/modules/global/manage.sh +154 -0
  142. package/internal/modules/handoff/manage.sh +54 -0
  143. package/internal/modules/mcp/manage.sh +83 -0
  144. package/internal/modules/ops/logs.sh +76 -0
  145. package/internal/modules/routines/manage.sh +55 -0
  146. package/internal/modules/runtime/assistant-autosave.sh +26 -0
  147. package/internal/modules/runtime/restart.sh +6 -0
  148. package/internal/modules/runtime/start.sh +283 -0
  149. package/internal/modules/runtime/status.sh +194 -0
  150. package/internal/modules/runtime/stop.sh +55 -0
  151. package/internal/modules/runtime/supervisor.sh +216 -0
  152. package/internal/modules/runtime/update.sh +26 -0
  153. package/package.json +41 -0
  154. package/runtime/config/.gitkeep +0 -0
  155. package/runtime/host/.gitkeep +0 -0
  156. package/runtime/host/healthcheck-codex-app-server.ts +22 -0
  157. package/runtime/host/ping-pong-codex-app-server.ts +66 -0
  158. package/runtime/host/probe-codex-app-server.ts +115 -0
  159. package/skills/archive-reader/SKILL.md +9 -0
  160. package/skills/feishu-production-debug/SKILL.md +37 -0
  161. package/skills/feishu-production-debug/references/feishu-debug-order.md +49 -0
  162. package/skills/feishu-production-debug/references/platform-permission-baseline.md +23 -0
  163. package/skills/issue-to-spec-triage/SKILL.md +44 -0
  164. package/skills/issue-to-spec-triage/references/triage-rules.md +66 -0
  165. package/skills/memory-digest/SKILL.md +9 -0
  166. package/skills/post-implementation-closure/SKILL.md +39 -0
  167. package/skills/post-implementation-closure/references/closure-checklist.md +45 -0
  168. package/skills/post-implementation-closure/references/doc-drift-map.md +49 -0
  169. package/skills/product-spec/SKILL.md +244 -0
  170. package/templates/env.example +5 -0
  171. package/templates/routines/nightly-memory-digest.yaml +10 -0
  172. package/templates/workspace/AGENTS.md +26 -0
@@ -0,0 +1,283 @@
1
+ #!/usr/bin/env bash
2
+ set -euo pipefail
3
+
4
+ SCRIPT_DIR=$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)
5
+ # shellcheck disable=SC1091
6
+ . "$SCRIPT_DIR/../../lib/common.sh"
7
+ work_ally_init_context
8
+ work_ally_install_bridge_dependencies
9
+
10
+ PREVIOUS_ASSISTANT_NAME="${WORK_ALLY_ASSISTANT_NAME:-}"
11
+ ASSISTANT_NAME=$(work_ally_resolve_assistant_name_from_args "$@")
12
+ work_ally_sync_workspace_root_from_assistant "$ASSISTANT_NAME"
13
+ work_ally_hydrate_assistant_context "$ASSISTANT_NAME"
14
+ work_ally_ensure_state_dirs
15
+ work_ally_ensure_env_defaults
16
+ work_ally_load_env
17
+ work_ally_load_assistant_state || true
18
+
19
+ CHANNEL_IMPL="${WORK_ALLY_CHANNEL_IMPL:-feishu}"
20
+ RUNTIME_MODE="${WORK_ALLY_RUNTIME_MODE:-codex}"
21
+ FOREGROUND="${WORK_ALLY_FOREGROUND:-0}"
22
+ ASSISTANT_GIT_REMOTE="${WORK_ALLY_ASSISTANT_GIT_REMOTE:-}"
23
+
24
+ if [ "$FOREGROUND" != "1" ] && work_ally_is_managed_ai_shell; then
25
+ work_ally_die "Current managed AI shell is not a reliable host for long-running background services. Please run $(work_ally_cmd_name) start in your own terminal."
26
+ fi
27
+
28
+ work_ally_require_assistant_profile_ready "$ASSISTANT_NAME"
29
+ ASSISTANT_HOME=$(work_ally_assistant_registered_home "$ASSISTANT_NAME")
30
+ ASSISTANT_CODEX_HOME=$(work_ally_assistant_registered_codex_home "$ASSISTANT_NAME")
31
+
32
+ if [ "$RUNTIME_MODE" = "codex" ]; then
33
+ work_ally_assert_codex_env_keys_available
34
+ fi
35
+
36
+ if [ "$CHANNEL_IMPL" = "feishu" ]; then
37
+ [ -n "${FEISHU_APP_ID:-}" ] || work_ally_die "FEISHU_APP_ID is required"
38
+ [ -n "${FEISHU_APP_SECRET:-}" ] || work_ally_die "FEISHU_APP_SECRET is required"
39
+ fi
40
+
41
+ notify_start_if_possible() {
42
+ local notice_output=""
43
+ local notice_status=0
44
+
45
+ if [ "${WORK_ALLY_CHANNEL_IMPL:-feishu}" != "feishu" ]; then
46
+ return 0
47
+ fi
48
+ if [ -z "${FEISHU_APP_ID:-}" ] || [ -z "${FEISHU_APP_SECRET:-}" ]; then
49
+ return 0
50
+ fi
51
+
52
+ notice_output=$(WORK_ALLY_SYSTEM_NOTICE_STRICT=1 node "$WORK_ALLY_IMPLEMENTATION_DIR/bridge/src/system-notify.ts" start 2>&1) || notice_status=$?
53
+ if [ "$notice_status" -eq 2 ]; then
54
+ if [ "${FOREGROUND:-0}" = "1" ]; then
55
+ work_ally_stop_bridge_processes
56
+ stop_autosave_if_needed
57
+ work_ally_release_channel_lock "$$"
58
+ work_ally_release_assistant_lock "$ASSISTANT_NAME" "$$"
59
+ else
60
+ stop_supervisor_if_needed
61
+ work_ally_stop_bridge_processes
62
+ stop_autosave_if_needed
63
+ work_ally_release_channel_lock
64
+ work_ally_release_assistant_lock "$ASSISTANT_NAME"
65
+ fi
66
+ work_ally_die "${notice_output:-Feishu 机器人当前还不能发消息。请先完成权限开通或审核发布,再重新执行 ally start。}"
67
+ fi
68
+ if [ "$notice_status" -ne 0 ]; then
69
+ work_ally_warn "startup notice probe failed: ${notice_output:-unknown error}"
70
+ fi
71
+ }
72
+
73
+ BRIDGE_PID_FILE=$(work_ally_pid_file bridge)
74
+ AUTOSAVE_PID_FILE=$(work_ally_pid_file assistant-autosave)
75
+ SUPERVISOR_PID_FILE=$(work_ally_pid_file supervisor)
76
+ BRIDGE_LOG=$(work_ally_log_file_for bridge)
77
+ AUTOSAVE_LOG=$(work_ally_log_file_for assistant-autosave)
78
+ SUPERVISOR_LOG=$(work_ally_log_file_for supervisor)
79
+ work_ally_prune_logs
80
+ DID_START_NEW=0
81
+
82
+ wait_for_background_ready() {
83
+ local attempt=0
84
+ while [ "$attempt" -lt 25 ]; do
85
+ local bridge_pid autosave_pid supervisor_pid
86
+ bridge_pid=$(work_ally_primary_bridge_pid || true)
87
+ if [ -n "$bridge_pid" ] && work_ally_is_pid_alive "$bridge_pid"; then
88
+ echo "$bridge_pid" > "$BRIDGE_PID_FILE"
89
+ autosave_pid=$(work_ally_read_pid "$AUTOSAVE_PID_FILE" || true)
90
+ if [ -n "$autosave_pid" ] && work_ally_is_pid_alive "$autosave_pid"; then
91
+ return 0
92
+ fi
93
+ fi
94
+ if [ -f "$SUPERVISOR_PID_FILE" ]; then
95
+ supervisor_pid=$(work_ally_read_pid "$SUPERVISOR_PID_FILE" || true)
96
+ if [ -n "$supervisor_pid" ] && ! work_ally_is_pid_alive "$supervisor_pid"; then
97
+ return 1
98
+ fi
99
+ fi
100
+ attempt=$((attempt + 1))
101
+ sleep 1
102
+ done
103
+ return 1
104
+ }
105
+
106
+ stop_autosave_if_needed() {
107
+ local pid
108
+ pid=$(work_ally_read_pid "$AUTOSAVE_PID_FILE" || true)
109
+ if [ -n "$pid" ] && work_ally_is_pid_alive "$pid"; then
110
+ work_ally_terminate_pid "$pid"
111
+ fi
112
+ rm -f "$AUTOSAVE_PID_FILE"
113
+ work_ally_write_health assistant-autosave stopped '{}'
114
+ }
115
+
116
+ stop_supervisor_if_needed() {
117
+ local pid
118
+ pid=$(work_ally_read_pid "$SUPERVISOR_PID_FILE" || true)
119
+ if [ -n "$pid" ] && work_ally_is_pid_alive "$pid"; then
120
+ work_ally_terminate_pid "$pid"
121
+ fi
122
+ rm -f "$SUPERVISOR_PID_FILE"
123
+ work_ally_write_health supervisor stopped '{}'
124
+ }
125
+
126
+ reset_running_services_for_assistant_switch() {
127
+ if [ -n "$PREVIOUS_ASSISTANT_NAME" ] && [ "$PREVIOUS_ASSISTANT_NAME" != "$ASSISTANT_NAME" ]; then
128
+ work_ally_note "Switching assistant from $PREVIOUS_ASSISTANT_NAME to $ASSISTANT_NAME"
129
+ stop_supervisor_if_needed
130
+ work_ally_stop_bridge_processes
131
+ stop_autosave_if_needed
132
+ fi
133
+ }
134
+
135
+ start_autosave_background() {
136
+ local existing_pid autosave_pid
137
+
138
+ work_ally_assistant_git_setup "$ASSISTANT_HOME" "$ASSISTANT_GIT_REMOTE" || true
139
+ work_ally_assistant_git_checkpoint "$ASSISTANT_HOME" "start assistant $ASSISTANT_NAME" || true
140
+
141
+ existing_pid=$(work_ally_read_pid "$AUTOSAVE_PID_FILE" || true)
142
+ if [ -n "$existing_pid" ] && work_ally_is_pid_alive "$existing_pid"; then
143
+ work_ally_write_health assistant-autosave running "{\"assistant\": \"$ASSISTANT_NAME\", \"pid\": ${existing_pid}, \"workspaceRoot\": \"$WORK_ALLY_WORKSPACE_ROOT\"}"
144
+ return 0
145
+ fi
146
+
147
+ nohup env \
148
+ WORK_ALLY_ASSISTANT_MODE="assistant" \
149
+ WORK_ALLY_ASSISTANT_NAME="$ASSISTANT_NAME" \
150
+ WORK_ALLY_ASSISTANT_HOME="$ASSISTANT_HOME" \
151
+ WORK_ALLY_ASSISTANT_CODEX_HOME="$ASSISTANT_CODEX_HOME" \
152
+ WORK_ALLY_ASSISTANT_GIT_REMOTE="$ASSISTANT_GIT_REMOTE" \
153
+ WORK_ALLY_WORKSPACE_ROOT="$WORK_ALLY_WORKSPACE_ROOT" \
154
+ WORK_ALLY_STATE_DIR="$WORK_ALLY_STATE_DIR" \
155
+ WORK_ALLY_INSTALL_ROOT="$WORK_ALLY_INSTALL_ROOT" \
156
+ WORK_ALLY_IMPLEMENTATION_DIR="$WORK_ALLY_IMPLEMENTATION_DIR" \
157
+ "$WORK_ALLY_IMPLEMENTATION_DIR/internal/modules/runtime/assistant-autosave.sh" >"$AUTOSAVE_LOG" 2>&1 &
158
+ echo $! > "$AUTOSAVE_PID_FILE"
159
+ sleep 1
160
+ autosave_pid=$(work_ally_read_pid "$AUTOSAVE_PID_FILE" || true)
161
+ if [ -n "$autosave_pid" ] && work_ally_is_pid_alive "$autosave_pid"; then
162
+ work_ally_write_health assistant-autosave running "{\"assistant\": \"$ASSISTANT_NAME\", \"pid\": ${autosave_pid}, \"workspaceRoot\": \"$WORK_ALLY_WORKSPACE_ROOT\"}"
163
+ return 0
164
+ fi
165
+ rm -f "$AUTOSAVE_PID_FILE"
166
+ work_ally_write_health assistant-autosave stopped '{}'
167
+ }
168
+
169
+ reset_running_services_for_assistant_switch
170
+ work_ally_assert_assistant_available "$ASSISTANT_NAME"
171
+ work_ally_write_assistant_state "$ASSISTANT_NAME" "$ASSISTANT_HOME" "$ASSISTANT_CODEX_HOME"
172
+
173
+ start_supervisor_background() {
174
+ local supervisor_pid existing_supervisor_pid
175
+
176
+ existing_supervisor_pid=$(work_ally_read_pid "$SUPERVISOR_PID_FILE" || true)
177
+ if [ -n "$existing_supervisor_pid" ] && work_ally_is_pid_alive "$existing_supervisor_pid" && [ "${WORK_ALLY_ENV_UPDATED:-0}" != "1" ]; then
178
+ local existing_bridge_pid
179
+ existing_bridge_pid=$(work_ally_primary_bridge_pid || true)
180
+ if [ -n "$existing_bridge_pid" ]; then
181
+ echo "$existing_bridge_pid" > "$BRIDGE_PID_FILE"
182
+ fi
183
+ if wait_for_background_ready; then
184
+ return 0
185
+ fi
186
+ work_ally_warn "Existing supervisor is unhealthy; restarting"
187
+ elif [ "${WORK_ALLY_ENV_UPDATED:-0}" = "1" ]; then
188
+ work_ally_note "Runtime config changed; restarting background supervisor"
189
+ fi
190
+
191
+ stop_supervisor_if_needed
192
+ work_ally_stop_bridge_processes
193
+ stop_autosave_if_needed
194
+ work_ally_assert_channel_available
195
+
196
+ work_ally_note "Starting work-ally supervisor"
197
+ nohup env \
198
+ CODEX_HOME="$ASSISTANT_CODEX_HOME" \
199
+ WORK_ALLY_ASSISTANT_MODE="assistant" \
200
+ WORK_ALLY_ASSISTANT_NAME="$ASSISTANT_NAME" \
201
+ WORK_ALLY_ASSISTANT_HOME="$ASSISTANT_HOME" \
202
+ WORK_ALLY_ASSISTANT_CODEX_HOME="$ASSISTANT_CODEX_HOME" \
203
+ WORK_ALLY_ASSISTANT_GIT_REMOTE="$ASSISTANT_GIT_REMOTE" \
204
+ WORK_ALLY_WORKSPACE_ROOT="$WORK_ALLY_WORKSPACE_ROOT" \
205
+ WORK_ALLY_STATE_DIR="$WORK_ALLY_STATE_DIR" \
206
+ WORK_ALLY_INSTALL_ROOT="$WORK_ALLY_INSTALL_ROOT" \
207
+ WORK_ALLY_IMPLEMENTATION_DIR="$WORK_ALLY_IMPLEMENTATION_DIR" \
208
+ "$WORK_ALLY_IMPLEMENTATION_DIR/internal/modules/runtime/supervisor.sh" >"$SUPERVISOR_LOG" 2>&1 &
209
+ echo $! > "$SUPERVISOR_PID_FILE"
210
+ supervisor_pid=$(work_ally_read_pid "$SUPERVISOR_PID_FILE" || true)
211
+ if [ -z "$supervisor_pid" ] || ! work_ally_is_pid_alive "$supervisor_pid"; then
212
+ rm -f "$SUPERVISOR_PID_FILE"
213
+ work_ally_die "Failed to start supervisor"
214
+ fi
215
+
216
+ if ! wait_for_background_ready; then
217
+ stop_supervisor_if_needed
218
+ work_ally_die "Failed to start background services"
219
+ fi
220
+
221
+ DID_START_NEW=1
222
+ }
223
+
224
+ run_bridge_foreground() {
225
+ local bridge_pid=""
226
+
227
+ cleanup() {
228
+ if [ -n "$bridge_pid" ] && work_ally_is_pid_alive "$bridge_pid"; then
229
+ kill "$bridge_pid" >/dev/null 2>&1 || true
230
+ wait "$bridge_pid" 2>/dev/null || true
231
+ fi
232
+ work_ally_stop_bridge_processes
233
+ stop_autosave_if_needed
234
+ work_ally_release_channel_lock "$$"
235
+ work_ally_release_assistant_lock "$ASSISTANT_NAME" "$$"
236
+ work_ally_assistant_git_checkpoint "$ASSISTANT_HOME" "stop assistant $ASSISTANT_NAME" || true
237
+ }
238
+
239
+ trap cleanup EXIT INT TERM
240
+ work_ally_assert_channel_available
241
+ work_ally_claim_assistant_lock "$ASSISTANT_NAME" "$$"
242
+ work_ally_claim_channel_lock "$$"
243
+ work_ally_note "Starting work-ally bridge in foreground"
244
+ start_autosave_background
245
+ env \
246
+ CODEX_HOME="$ASSISTANT_CODEX_HOME" \
247
+ WORK_ALLY_ASSISTANT_MODE="assistant" \
248
+ WORK_ALLY_ASSISTANT_NAME="$ASSISTANT_NAME" \
249
+ WORK_ALLY_ASSISTANT_HOME="$ASSISTANT_HOME" \
250
+ WORK_ALLY_ASSISTANT_CODEX_HOME="$ASSISTANT_CODEX_HOME" \
251
+ WORK_ALLY_WORKSPACE_ROOT="$WORK_ALLY_WORKSPACE_ROOT" \
252
+ WORK_ALLY_STATE_DIR="$WORK_ALLY_STATE_DIR" \
253
+ WORK_ALLY_INSTALL_ROOT="$WORK_ALLY_INSTALL_ROOT" \
254
+ WORK_ALLY_IMPLEMENTATION_DIR="$WORK_ALLY_IMPLEMENTATION_DIR" \
255
+ node "$WORK_ALLY_IMPLEMENTATION_DIR/bridge/src/server.ts" >"$BRIDGE_LOG" 2>&1 &
256
+ bridge_pid=$!
257
+ echo "$bridge_pid" > "$BRIDGE_PID_FILE"
258
+ sleep 2
259
+ if ! work_ally_is_pid_alive "$bridge_pid"; then
260
+ work_ally_release_channel_lock "$$"
261
+ work_ally_release_assistant_lock "$ASSISTANT_NAME" "$$"
262
+ work_ally_die "Failed to start bridge"
263
+ fi
264
+ work_ally_write_channel_lock "$bridge_pid"
265
+ work_ally_write_assistant_lock "$ASSISTANT_NAME" "$bridge_pid"
266
+ notify_start_if_possible
267
+ work_ally_ok "work-ally started"
268
+ wait "$bridge_pid"
269
+ }
270
+
271
+ if [ "$FOREGROUND" = "1" ]; then
272
+ work_ally_stop_bridge_processes
273
+ stop_autosave_if_needed
274
+ run_bridge_foreground
275
+ else
276
+ start_supervisor_background
277
+ if [ "$DID_START_NEW" = "1" ]; then
278
+ notify_start_if_possible
279
+ work_ally_ok "work-ally started"
280
+ else
281
+ work_ally_ok "work-ally already running"
282
+ fi
283
+ fi
@@ -0,0 +1,194 @@
1
+ #!/usr/bin/env bash
2
+ set -euo pipefail
3
+
4
+ SCRIPT_DIR=$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)
5
+ # shellcheck disable=SC1091
6
+ . "$SCRIPT_DIR/../../lib/common.sh"
7
+ work_ally_init_context
8
+ if [ -z "${WORK_ALLY_ASSISTANT_NAME:-}" ]; then
9
+ WORK_ALLY_ASSISTANT_NAME=$(work_ally_resolve_assistant_name_from_args "$@" || true)
10
+ fi
11
+ if [ -n "${WORK_ALLY_ASSISTANT_NAME:-}" ]; then
12
+ work_ally_sync_workspace_root_from_assistant "$WORK_ALLY_ASSISTANT_NAME"
13
+ work_ally_hydrate_assistant_context "$WORK_ALLY_ASSISTANT_NAME"
14
+ fi
15
+ work_ally_ensure_state_dirs
16
+ entry_install_root="${WORK_ALLY_INSTALL_ROOT:-}"
17
+ entry_implementation_dir="${WORK_ALLY_IMPLEMENTATION_DIR:-}"
18
+ entry_install_channel="${WORK_ALLY_INSTALL_CHANNEL:-unknown}"
19
+ work_ally_load_env
20
+ work_ally_load_assistant_state || true
21
+ active_implementation_dir="${WORK_ALLY_IMPLEMENTATION_DIR:-}"
22
+ active_install_channel="${WORK_ALLY_INSTALL_CHANNEL:-unknown}"
23
+
24
+ if [ -n "${WORK_ALLY_ASSISTANT_NAME:-}" ] && work_ally_registry_has_assistant "$WORK_ALLY_ASSISTANT_NAME"; then
25
+ export WORK_ALLY_ASSISTANT_HOME="${WORK_ALLY_ASSISTANT_HOME:-$(work_ally_assistant_registered_home "$WORK_ALLY_ASSISTANT_NAME")}"
26
+ export WORK_ALLY_ASSISTANT_CODEX_HOME="${WORK_ALLY_ASSISTANT_CODEX_HOME:-$(work_ally_assistant_registered_codex_home "$WORK_ALLY_ASSISTANT_NAME")}"
27
+ fi
28
+
29
+ json_field() {
30
+ local file="$1"
31
+ local key="$2"
32
+ [ -f "$file" ] || return 0
33
+ sed -nE "s/.*\"${key}\"[[:space:]]*:[[:space:]]*\"([^\"]*)\".*/\\1/p" "$file" | head -n 1
34
+ }
35
+
36
+ json_number_field() {
37
+ local file="$1"
38
+ local key="$2"
39
+ [ -f "$file" ] || return 0
40
+ sed -nE "s/.*\"${key}\"[[:space:]]*:[[:space:]]*([0-9]+).*/\\1/p" "$file" | head -n 1
41
+ }
42
+
43
+ print_health_details() {
44
+ local name="$1"
45
+ local file="$2"
46
+ [ -f "$file" ] || return 0
47
+
48
+ local health_status updated owner listen workspace_root channel runtime pid
49
+ local delivery_status delivery_channel delivery_kind delivery_code delivery_message delivery_guidance
50
+ health_status=$(json_field "$file" status)
51
+ updated=$(json_field "$file" updated_at)
52
+ [ -n "$updated" ] || updated=$(json_field "$file" at)
53
+ owner=$(json_field "$file" owner)
54
+ listen=$(json_field "$file" listen)
55
+ workspace_root=$(json_field "$file" workspaceRoot)
56
+ channel=$(json_field "$file" channel)
57
+ runtime=$(json_field "$file" runtime)
58
+ pid=$(json_number_field "$file" pid)
59
+ delivery_status=$(json_field "$file" delivery_status)
60
+ delivery_channel=$(json_field "$file" delivery_channel)
61
+ delivery_kind=$(json_field "$file" delivery_error_kind)
62
+ delivery_code=$(json_field "$file" delivery_error_code)
63
+ delivery_message=$(json_field "$file" delivery_message)
64
+ delivery_guidance=$(json_field "$file" delivery_guidance)
65
+
66
+ local effective_status="${health_status:-unknown}"
67
+ if [ "$name" = "bridge" ]; then
68
+ local live_pids
69
+ live_pids=$(join_bridge_pids)
70
+ if { [ "$health_status" = "running" ] || [ "$health_status" = "degraded" ]; } \
71
+ && [ "$live_pids" = "none" ] \
72
+ && { [ -z "$pid" ] || ! work_ally_is_pid_alive "$pid"; }; then
73
+ effective_status="stale (${health_status})"
74
+ fi
75
+ fi
76
+ if [ "$name" = "supervisor" ]; then
77
+ local supervisor_state
78
+ supervisor_state=$(work_ally_process_status supervisor)
79
+ if [ "$health_status" = "running" ] && [[ "$supervisor_state" != running* ]]; then
80
+ effective_status="stale (${health_status})"
81
+ fi
82
+ fi
83
+
84
+ printf '%s health: %s\n' "$name" "$file"
85
+ printf '%s health status: %s\n' "$name" "$effective_status"
86
+ printf '%s health updated: %s\n' "$name" "${updated:-unknown}"
87
+ [ -n "$owner" ] && printf '%s owner: %s\n' "$name" "$owner"
88
+ [ -n "$listen" ] && printf '%s listen (health): %s\n' "$name" "$listen"
89
+ [ -n "$pid" ] && printf '%s pid (health): %s\n' "$name" "$pid"
90
+ [ -n "$workspace_root" ] && printf '%s workspace (health): %s\n' "$name" "$workspace_root"
91
+ [ -n "$channel" ] && printf '%s channel (health): %s\n' "$name" "$channel"
92
+ [ -n "$runtime" ] && printf '%s runtime (health): %s\n' "$name" "$runtime"
93
+ if [ "$name" = "bridge" ] && [ -n "$delivery_status" ]; then
94
+ printf '%s delivery status: %s\n' "$name" "$delivery_status"
95
+ [ -n "$delivery_channel" ] && printf '%s delivery channel: %s\n' "$name" "$delivery_channel"
96
+ [ -n "$delivery_kind" ] && printf '%s delivery issue: %s\n' "$name" "$delivery_kind"
97
+ [ -n "$delivery_code" ] && printf '%s delivery code: %s\n' "$name" "$delivery_code"
98
+ [ -n "$delivery_message" ] && printf '%s delivery message: %s\n' "$name" "$delivery_message"
99
+ [ -n "$delivery_guidance" ] && printf '%s delivery guidance: %s\n' "$name" "$delivery_guidance"
100
+ fi
101
+ return 0
102
+ }
103
+
104
+ pid_or_none() {
105
+ local file="$1"
106
+ local pid
107
+ pid=$(work_ally_read_pid "$file" || true)
108
+ printf '%s\n' "${pid:-none}"
109
+ }
110
+
111
+ join_bridge_pids() {
112
+ local pids
113
+ pids=$(work_ally_list_bridge_pids || true)
114
+ if [ -z "$pids" ]; then
115
+ printf 'none\n'
116
+ return 0
117
+ fi
118
+ printf '%s\n' "$pids" | awk 'NF' | paste -sd ',' -
119
+ }
120
+
121
+ print_channel_lock_details() {
122
+ local lock_file lock_status lock_workspace lock_pid
123
+ lock_file=$(work_ally_channel_lock_file || true)
124
+ [ -n "$lock_file" ] || return 0
125
+
126
+ lock_status=$(work_ally_channel_lock_status)
127
+ printf 'channel lock file: %s\n' "$lock_file"
128
+ printf 'channel lock status: %s\n' "$lock_status"
129
+
130
+ if [ -f "$lock_file" ]; then
131
+ lock_workspace=$(work_ally_channel_lock_field "$lock_file" workspace_root || true)
132
+ lock_pid=$(work_ally_channel_lock_field "$lock_file" pid || true)
133
+ [ -n "$lock_workspace" ] && printf 'channel lock workspace: %s\n' "$lock_workspace"
134
+ [ -n "$lock_pid" ] && printf 'channel lock pid: %s\n' "$lock_pid"
135
+ fi
136
+ }
137
+
138
+ print_assistant_lock_details() {
139
+ [ -n "${WORK_ALLY_ASSISTANT_NAME:-}" ] || return 0
140
+ local lock_file lock_status lock_workspace lock_pid
141
+ lock_file=$(work_ally_assistant_lock_file "$WORK_ALLY_ASSISTANT_NAME")
142
+ lock_status=$(work_ally_assistant_lock_status "$WORK_ALLY_ASSISTANT_NAME")
143
+ printf 'assistant lock file: %s\n' "$lock_file"
144
+ printf 'assistant lock status: %s\n' "$lock_status"
145
+ if [ -f "$lock_file" ]; then
146
+ lock_workspace=$(work_ally_assistant_lock_field "$lock_file" workspace_root || true)
147
+ lock_pid=$(work_ally_assistant_lock_field "$lock_file" pid || true)
148
+ [ -n "$lock_workspace" ] && printf 'assistant lock workspace: %s\n' "$lock_workspace"
149
+ [ -n "$lock_pid" ] && printf 'assistant lock pid: %s\n' "$lock_pid"
150
+ fi
151
+ }
152
+
153
+ supervisor_pid_file=$(work_ally_pid_file supervisor)
154
+ bridge_pid_file=$(work_ally_pid_file bridge)
155
+ autosave_pid_file=$(work_ally_pid_file assistant-autosave)
156
+ supervisor_health=$(work_ally_health_file supervisor)
157
+ bridge_health=$(work_ally_health_file bridge)
158
+ autosave_health=$(work_ally_health_file assistant-autosave)
159
+
160
+ printf 'mode: %s\n' "${WORK_ALLY_ASSISTANT_MODE:-native}"
161
+ printf 'assistant: %s\n' "${WORK_ALLY_ASSISTANT_NAME:-none}"
162
+ printf 'assistant home: %s\n' "${WORK_ALLY_ASSISTANT_HOME:-none}"
163
+ printf 'assistant codex home: %s\n' "${WORK_ALLY_ASSISTANT_CODEX_HOME:-${CODEX_HOME:-none}}"
164
+ if [ -n "${WORK_ALLY_ASSISTANT_HOME:-}" ]; then
165
+ printf 'assistant git remote: %s\n' "$(work_ally_assistant_git_origin_url "$WORK_ALLY_ASSISTANT_HOME" || true)"
166
+ printf 'assistant git branch: %s\n' "$(work_ally_assistant_git_branch "$WORK_ALLY_ASSISTANT_HOME")"
167
+ fi
168
+
169
+ printf 'workspace: %s\n' "$WORK_ALLY_WORKSPACE_ROOT"
170
+ printf 'install channel: %s\n' "$entry_install_channel"
171
+ printf 'implementation: %s\n' "$entry_implementation_dir"
172
+ printf 'install root: %s\n' "$entry_install_root"
173
+ if [ "$active_implementation_dir" != "$entry_implementation_dir" ] || [ "$active_install_channel" != "$entry_install_channel" ]; then
174
+ printf 'active install channel: %s\n' "$active_install_channel"
175
+ printf 'active implementation: %s\n' "$active_implementation_dir"
176
+ fi
177
+ printf 'state: %s\n' "$WORK_ALLY_STATE_DIR"
178
+ printf 'supervisor: %s\n' "$(work_ally_process_status supervisor)"
179
+ printf 'supervisor pid file: %s\n' "$supervisor_pid_file"
180
+ printf 'supervisor pid: %s\n' "$(pid_or_none "$supervisor_pid_file")"
181
+ print_health_details supervisor "$supervisor_health"
182
+
183
+ printf 'bridge: %s\n' "$(work_ally_process_status bridge)"
184
+ printf 'bridge pid file: %s\n' "$bridge_pid_file"
185
+ printf 'bridge pid: %s\n' "$(pid_or_none "$bridge_pid_file")"
186
+ printf 'bridge live pids: %s\n' "$(join_bridge_pids)"
187
+ print_health_details bridge "$bridge_health"
188
+
189
+ printf 'assistant autosave: %s\n' "$(work_ally_process_status assistant-autosave)"
190
+ printf 'assistant autosave pid file: %s\n' "$autosave_pid_file"
191
+ printf 'assistant autosave pid: %s\n' "$(pid_or_none "$autosave_pid_file")"
192
+ print_health_details assistant-autosave "$autosave_health"
193
+ print_channel_lock_details
194
+ print_assistant_lock_details
@@ -0,0 +1,55 @@
1
+ #!/usr/bin/env bash
2
+ set -euo pipefail
3
+
4
+ SCRIPT_DIR=$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)
5
+ # shellcheck disable=SC1091
6
+ . "$SCRIPT_DIR/../../lib/common.sh"
7
+ work_ally_init_context
8
+ if [ -z "${WORK_ALLY_ASSISTANT_NAME:-}" ]; then
9
+ WORK_ALLY_ASSISTANT_NAME=$(work_ally_resolve_assistant_name_from_args "$@" || true)
10
+ fi
11
+ if [ -n "${WORK_ALLY_ASSISTANT_NAME:-}" ]; then
12
+ work_ally_sync_workspace_root_from_assistant "$WORK_ALLY_ASSISTANT_NAME"
13
+ work_ally_hydrate_assistant_context "$WORK_ALLY_ASSISTANT_NAME"
14
+ fi
15
+ work_ally_ensure_state_dirs
16
+ work_ally_load_env
17
+ work_ally_load_assistant_state || true
18
+
19
+ notify_stop_if_possible() {
20
+ if [ "${WORK_ALLY_CHANNEL_IMPL:-feishu}" != "feishu" ]; then
21
+ return 0
22
+ fi
23
+ if [ -z "${FEISHU_APP_ID:-}" ] || [ -z "${FEISHU_APP_SECRET:-}" ]; then
24
+ return 0
25
+ fi
26
+ node "$WORK_ALLY_IMPLEMENTATION_DIR/bridge/src/system-notify.ts" stop >/dev/null 2>&1 || true
27
+ }
28
+
29
+ stop_one() {
30
+ local name="$1"
31
+ local pid_file
32
+ pid_file=$(work_ally_pid_file "$name")
33
+
34
+ if [ ! -f "$pid_file" ]; then
35
+ return 0
36
+ fi
37
+
38
+ local pid
39
+ pid=$(work_ally_read_pid "$pid_file" || true)
40
+ if work_ally_is_pid_alive "$pid"; then
41
+ work_ally_terminate_pid "$pid"
42
+ fi
43
+ rm -f "$pid_file"
44
+ work_ally_write_health "$name" stopped '{}'
45
+ }
46
+
47
+ notify_stop_if_possible
48
+ stop_one supervisor
49
+ work_ally_stop_bridge_processes
50
+ stop_one assistant-autosave
51
+ if [ -n "${WORK_ALLY_ASSISTANT_HOME:-}" ]; then
52
+ work_ally_assistant_git_setup "$WORK_ALLY_ASSISTANT_HOME" "${WORK_ALLY_ASSISTANT_GIT_REMOTE:-}" || true
53
+ work_ally_assistant_git_checkpoint "$WORK_ALLY_ASSISTANT_HOME" "stop assistant ${WORK_ALLY_ASSISTANT_NAME:-unknown}" || true
54
+ fi
55
+ work_ally_ok "work-ally stopped"