@profoundlogic/coderflow-server 0.4.1 → 0.4.2

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 (148) hide show
  1. package/dist/base-image/Dockerfile +2 -1
  2. package/dist/base-image/entrypoint.sh +11 -1
  3. package/dist/base-image/rewind-files.sh +197 -0
  4. package/dist/base-image/sync-repos.sh +53 -3
  5. package/dist/coder-server.js +1 -1
  6. package/dist/config.js +1 -1
  7. package/dist/lib/agent-keepalive.js +1 -1
  8. package/dist/lib/agent-models.js +1 -1
  9. package/dist/lib/api-keys.js +1 -1
  10. package/dist/lib/apiKeys.js +1 -1
  11. package/dist/lib/app-server-ports.js +1 -1
  12. package/dist/lib/auto-judge.js +1 -1
  13. package/dist/lib/basic-auth.js +1 -1
  14. package/dist/lib/build-history.js +1 -1
  15. package/dist/lib/build-output-service.js +1 -1
  16. package/dist/lib/build-scheduler.js +1 -1
  17. package/dist/lib/build-service.js +1 -1
  18. package/dist/lib/ca-certificates.js +1 -1
  19. package/dist/lib/claude-oauth-refresh.js +1 -1
  20. package/dist/lib/cli/build.js +1 -1
  21. package/dist/lib/cli/config-command.js +1 -1
  22. package/dist/lib/cli/config.js +1 -1
  23. package/dist/lib/cli/create-user.js +1 -1
  24. package/dist/lib/cli/init.js +1 -1
  25. package/dist/lib/cli/jira.js +1 -1
  26. package/dist/lib/cli/license.js +1 -1
  27. package/dist/lib/cli/server-manager.js +1 -1
  28. package/dist/lib/config-migration.js +1 -1
  29. package/dist/lib/container-credential-sync.js +1 -1
  30. package/dist/lib/container-tokens.js +1 -1
  31. package/dist/lib/data-dir.js +1 -1
  32. package/dist/lib/deployment-history.js +1 -1
  33. package/dist/lib/deployment-service.js +1 -1
  34. package/dist/lib/docker-utils.js +1 -1
  35. package/dist/lib/email.js +1 -1
  36. package/dist/lib/emailTemplates.js +1 -1
  37. package/dist/lib/entitlement.js +1 -1
  38. package/dist/lib/fetch-utils.js +1 -1
  39. package/dist/lib/git-commit-details-route.js +1 -1
  40. package/dist/lib/git-history-diff-guardrails.js +1 -1
  41. package/dist/lib/git-provider-service.js +1 -1
  42. package/dist/lib/git-provider-setup/github-setup-handler.js +1 -1
  43. package/dist/lib/git-provider-setup/index.js +1 -1
  44. package/dist/lib/git-provider-setup/setup-factory.js +1 -1
  45. package/dist/lib/git-provider-setup/setup-interface.js +1 -1
  46. package/dist/lib/git-providers/azure-devops-provider.js +1 -1
  47. package/dist/lib/git-providers/github-app-provider.js +1 -1
  48. package/dist/lib/git-providers/index.js +1 -1
  49. package/dist/lib/git-providers/provider-factory.js +1 -1
  50. package/dist/lib/git-providers/provider-interface.js +1 -1
  51. package/dist/lib/github-urls.js +1 -1
  52. package/dist/lib/group-objective-linking.js +1 -1
  53. package/dist/lib/jira-client.js +1 -1
  54. package/dist/lib/judge-blinding.js +1 -1
  55. package/dist/lib/logger.js +1 -1
  56. package/dist/lib/model-fetcher.js +1 -1
  57. package/dist/lib/notifications.js +1 -1
  58. package/dist/lib/objective-context.js +1 -0
  59. package/dist/lib/oidc-auth.js +1 -1
  60. package/dist/lib/oidc-device-flow.js +1 -1
  61. package/dist/lib/passwordTokens.js +1 -1
  62. package/dist/lib/pin-cascade.js +1 -1
  63. package/dist/lib/provider-accounts.js +1 -1
  64. package/dist/lib/provider-oauth.js +1 -1
  65. package/dist/lib/provider-profile.js +1 -1
  66. package/dist/lib/provider-token-refresh.js +1 -1
  67. package/dist/lib/request-url.js +1 -1
  68. package/dist/lib/rewind.js +1 -1
  69. package/dist/lib/roles.js +1 -1
  70. package/dist/lib/secrets.js +1 -1
  71. package/dist/lib/setup-repo-git-auth.js +1 -1
  72. package/dist/lib/state-capture.js +1 -1
  73. package/dist/lib/static-files.js +1 -1
  74. package/dist/lib/task-name-format.js +1 -1
  75. package/dist/lib/task-name-generator.js +1 -1
  76. package/dist/lib/user-git-oauth.js +1 -1
  77. package/dist/lib/user-git-tokens.js +1 -1
  78. package/dist/lib/users.js +1 -1
  79. package/dist/middleware/requireAuth.js +1 -1
  80. package/dist/middleware/requireInit.js +1 -1
  81. package/dist/middleware/requirePermission.js +1 -1
  82. package/dist/package.json +1 -1
  83. package/dist/routes/apiKeys.js +1 -1
  84. package/dist/routes/auth-oidc.js +1 -1
  85. package/dist/routes/auth.js +1 -1
  86. package/dist/routes/build.js +1 -1
  87. package/dist/routes/containers.js +1 -1
  88. package/dist/routes/deploy-task.js +1 -1
  89. package/dist/routes/environment-management.js +1 -1
  90. package/dist/routes/environments.js +1 -1
  91. package/dist/routes/external-skills.js +1 -1
  92. package/dist/routes/git-credentials.js +1 -1
  93. package/dist/routes/git-oauth.js +1 -1
  94. package/dist/routes/git-provider-setup.js +1 -1
  95. package/dist/routes/health.js +1 -1
  96. package/dist/routes/jira.js +1 -1
  97. package/dist/routes/objective-management.js +1 -1
  98. package/dist/routes/password.js +1 -1
  99. package/dist/routes/prompt.js +1 -1
  100. package/dist/routes/provider-auth.js +1 -1
  101. package/dist/routes/qa.js +1 -1
  102. package/dist/routes/settings.js +1 -1
  103. package/dist/routes/skill-management.js +1 -1
  104. package/dist/routes/skills.js +1 -1
  105. package/dist/routes/tasks.js +1 -1
  106. package/dist/routes/templates.js +1 -1
  107. package/dist/routes/test-task.js +1 -1
  108. package/dist/routes/test.js +1 -1
  109. package/dist/routes/users.js +1 -1
  110. package/dist/routes/visualizations.js +1 -1
  111. package/dist/scripts/create-user.js +1 -1
  112. package/dist/scripts/migrate-config-to-data-dir.js +1 -1
  113. package/dist/shipped-skills/objective-management/SKILL.md +83 -0
  114. package/dist/start.js +1 -1
  115. package/dist/web-ui/public/activity-detail-modal.js +1 -1
  116. package/dist/web-ui/public/activity-feed.js +1 -1
  117. package/dist/web-ui/public/activity-formatters.js +1 -1
  118. package/dist/web-ui/public/agent-event-parser.js +1 -1
  119. package/dist/web-ui/public/app.js +1 -1
  120. package/dist/web-ui/public/approve-dialog.js +1 -1
  121. package/dist/web-ui/public/comments-widget.js +1 -1
  122. package/dist/web-ui/public/diff-utils.js +1 -1
  123. package/dist/web-ui/public/environments.js +1 -1
  124. package/dist/web-ui/public/feedback-widget.js +1 -1
  125. package/dist/web-ui/public/git-history-lazy-utils.js +1 -1
  126. package/dist/web-ui/public/git-history.js +1 -1
  127. package/dist/web-ui/public/git-status.js +1 -1
  128. package/dist/web-ui/public/index.html +21 -0
  129. package/dist/web-ui/public/index.js +1 -1
  130. package/dist/web-ui/public/login.js +1 -1
  131. package/dist/web-ui/public/markdown-editor.js +1 -1
  132. package/dist/web-ui/public/markdown-file-editor.js +1 -1
  133. package/dist/web-ui/public/modal-maximize.js +1 -1
  134. package/dist/web-ui/public/notifications.js +1 -1
  135. package/dist/web-ui/public/pr-dialog.js +1 -1
  136. package/dist/web-ui/public/server-health.js +1 -1
  137. package/dist/web-ui/public/settings.js +1 -1
  138. package/dist/web-ui/public/setup-password.js +1 -1
  139. package/dist/web-ui/public/skills.js +1 -1
  140. package/dist/web-ui/public/sse-client.js +1 -1
  141. package/dist/web-ui/public/sse-shared-worker.js +1 -1
  142. package/dist/web-ui/public/styles.css +80 -0
  143. package/dist/web-ui/public/task.js +1 -1
  144. package/dist/web-ui/public/terminal.js +1 -1
  145. package/dist/web-ui/public/theme.js +1 -1
  146. package/dist/web-ui/public/users.js +1 -1
  147. package/dist/web-ui/public/variant-grouping.js +1 -1
  148. package/package.json +1 -1
@@ -176,11 +176,12 @@ WORKDIR /workspace
176
176
  # Copy entrypoint script and AI agent wrapper
177
177
  COPY entrypoint.sh /usr/local/bin/entrypoint.sh
178
178
  COPY sync-repos.sh /usr/local/bin/sync-repos.sh
179
+ COPY rewind-files.sh /usr/local/bin/rewind-files.sh
179
180
  COPY agent-wrapper.sh /usr/local/bin/agent-wrapper.sh
180
181
  COPY start-code-server.sh /usr/local/bin/start-code-server.sh
181
182
  COPY apply-local-state.sh /usr/local/bin/apply-local-state.sh
182
183
  COPY coder-git-credential-helper /usr/local/bin/coder-git-credential-helper
183
- RUN chmod +x /usr/local/bin/entrypoint.sh /usr/local/bin/sync-repos.sh /usr/local/bin/agent-wrapper.sh /usr/local/bin/start-code-server.sh /usr/local/bin/apply-local-state.sh /usr/local/bin/coder-git-credential-helper
184
+ RUN chmod +x /usr/local/bin/entrypoint.sh /usr/local/bin/sync-repos.sh /usr/local/bin/rewind-files.sh /usr/local/bin/agent-wrapper.sh /usr/local/bin/start-code-server.sh /usr/local/bin/apply-local-state.sh /usr/local/bin/coder-git-credential-helper
184
185
 
185
186
  # Expose code-server port
186
187
  EXPOSE 8080
@@ -715,7 +715,17 @@ generate_task_json() {
715
715
  escaped_command="${escaped_command//$'\r'/\\r}" # Escape carriage returns
716
716
  escaped_command="${escaped_command//$'\t'/\\t}" # Escape tabs
717
717
 
718
+ # Preserve turn_patches accumulated by auto-sync from previous runs
719
+ local existing_turn_patches='null'
720
+ if [ -f "$TASK_OUTPUT_DIR/task.json" ]; then
721
+ existing_turn_patches=$(jq -c '.turn_patches // null' "$TASK_OUTPUT_DIR/task.json" 2>/dev/null || echo 'null')
722
+ fi
723
+
718
724
  # Create task.json in the output directory
725
+ local turn_patches_field=""
726
+ if [ "$existing_turn_patches" != "null" ] && [ -n "$existing_turn_patches" ]; then
727
+ turn_patches_field=',"turn_patches": '"$existing_turn_patches"
728
+ fi
719
729
  cat > "$TASK_OUTPUT_DIR/task.json" <<EOF
720
730
  {
721
731
  "task_id": "$TASK_ID",
@@ -733,7 +743,7 @@ generate_task_json() {
733
743
  "log_path": "log.txt",
734
744
  "instructions_path": "task-instructions.md",
735
745
  "commit_message": "$commit_message",
736
- "repos_changed": $repos_changed
746
+ "repos_changed": $repos_changed$turn_patches_field
737
747
  }
738
748
  EOF
739
749
 
@@ -0,0 +1,197 @@
1
+ #!/bin/bash
2
+ # Rewind workspace files to a specific turn snapshot.
3
+ # Resets each repository to committed state, then reapplies the turn patch.
4
+
5
+ set -o pipefail
6
+
7
+ TASK_OUTPUT_DIR="${TASK_OUTPUT_DIR:-/task-output}"
8
+ WORKSPACE_DIR="${WORKSPACE_DIR:-/workspace}"
9
+ TARGET_TURN="${TARGET_TURN:-}"
10
+ turn_dir="$TASK_OUTPUT_DIR/patches/turn-$TARGET_TURN"
11
+
12
+ if ! [[ "$TARGET_TURN" =~ ^[0-9]+$ ]]; then
13
+ echo "{\"target_turn\":0,\"success\":false,\"error\":\"TARGET_TURN must be a non-negative integer\",\"repos\":[]}"
14
+ exit 1
15
+ fi
16
+
17
+ temp_dir=$(mktemp -d)
18
+ trap "rm -rf $temp_dir" EXIT
19
+
20
+ overall_success=true
21
+ repo_index=0
22
+
23
+ emit_repo_result() {
24
+ local output_file="$1"
25
+ local repo_name="$2"
26
+ local repo_path="$3"
27
+ local has_patch="$4"
28
+ local applied="$5"
29
+ local success="$6"
30
+ local error_message="$7"
31
+ local files_changed="$8"
32
+ local lines_added="$9"
33
+ local lines_deleted="${10}"
34
+
35
+ jq -n \
36
+ --arg name "$repo_name" \
37
+ --arg path "$repo_path" \
38
+ --argjson has_patch "$has_patch" \
39
+ --argjson applied "$applied" \
40
+ --argjson success "$success" \
41
+ --arg error "$error_message" \
42
+ --argjson files_changed "$files_changed" \
43
+ --argjson lines_added "$lines_added" \
44
+ --argjson lines_deleted "$lines_deleted" \
45
+ '{
46
+ name: $name,
47
+ path: $path,
48
+ has_patch: $has_patch,
49
+ applied: $applied,
50
+ success: $success,
51
+ error: ($error | if . == "" then null else . end),
52
+ files_changed: $files_changed,
53
+ lines_added: $lines_added,
54
+ lines_deleted: $lines_deleted
55
+ }' > "$output_file"
56
+ }
57
+
58
+ process_repo() {
59
+ local repo_name="$1"
60
+ local repo_path="$2"
61
+ local output_file="$3"
62
+ local patch_file="$turn_dir/$repo_name.patch"
63
+
64
+ local has_patch=false
65
+ local applied=false
66
+ local success=true
67
+ local error_message=""
68
+ local files_changed=0
69
+ local lines_added=0
70
+ local lines_deleted=0
71
+
72
+ if [ ! -d "$repo_path/.git" ]; then
73
+ success=false
74
+ error_message="Repository is missing .git directory"
75
+ emit_repo_result "$output_file" "$repo_name" "$repo_path" "$has_patch" "$applied" "$success" "$error_message" "$files_changed" "$lines_added" "$lines_deleted"
76
+ return 1
77
+ fi
78
+
79
+ cd "$repo_path" || {
80
+ success=false
81
+ error_message="Failed to access repository directory"
82
+ emit_repo_result "$output_file" "$repo_name" "$repo_path" "$has_patch" "$applied" "$success" "$error_message" "$files_changed" "$lines_added" "$lines_deleted"
83
+ return 1
84
+ }
85
+
86
+ if ! git reset --hard HEAD >/dev/null 2>&1; then
87
+ success=false
88
+ error_message="git reset --hard failed"
89
+ fi
90
+
91
+ if $success && ! git clean -fd >/dev/null 2>&1; then
92
+ success=false
93
+ error_message="git clean -fd failed"
94
+ fi
95
+
96
+ if $success && [ -f "$patch_file" ] && [ -s "$patch_file" ]; then
97
+ has_patch=true
98
+ if git apply --whitespace=nowarn "$patch_file" >/dev/null 2>&1; then
99
+ applied=true
100
+ else
101
+ success=false
102
+ error_message="git apply failed for turn patch"
103
+ fi
104
+ fi
105
+
106
+ if $success && ! git add -A >/dev/null 2>&1; then
107
+ success=false
108
+ error_message="git add -A failed"
109
+ fi
110
+
111
+ if $success; then
112
+ local stat_output
113
+ stat_output=$(git diff --cached --stat 2>/dev/null | tail -1)
114
+ if [ -n "$stat_output" ]; then
115
+ files_changed=$(echo "$stat_output" | grep -oE '[0-9]+ file' | grep -oE '[0-9]+' || echo "0")
116
+ lines_added=$(echo "$stat_output" | grep -oE '[0-9]+ insertion' | grep -oE '[0-9]+' || echo "0")
117
+ lines_deleted=$(echo "$stat_output" | grep -oE '[0-9]+ deletion' | grep -oE '[0-9]+' || echo "0")
118
+ fi
119
+ files_changed=${files_changed:-0}
120
+ lines_added=${lines_added:-0}
121
+ lines_deleted=${lines_deleted:-0}
122
+ fi
123
+
124
+ emit_repo_result "$output_file" "$repo_name" "$repo_path" "$has_patch" "$applied" "$success" "$error_message" "$files_changed" "$lines_added" "$lines_deleted"
125
+ $success
126
+ }
127
+
128
+ process_repo_entry() {
129
+ local repo_name="$1"
130
+ local repo_path="$2"
131
+ local output_file="$temp_dir/repo_$repo_index.json"
132
+
133
+ process_repo "$repo_name" "$repo_path" "$output_file" || overall_success=false
134
+ repo_index=$((repo_index + 1))
135
+ }
136
+
137
+ # Iterate configured repositories, or scan workspace if no config was provided.
138
+ if [ -n "$REPOS_CONFIG" ]; then
139
+ repo_count=$(echo "$REPOS_CONFIG" | jq 'length' 2>/dev/null)
140
+ if [ -n "$repo_count" ] && [ "$repo_count" -gt 0 ]; then
141
+ for index in $(seq 0 $((repo_count - 1))); do
142
+ repo_json=$(echo "$REPOS_CONFIG" | jq -c ".[$index]" 2>/dev/null)
143
+ [ -z "$repo_json" ] || [ "$repo_json" = "null" ] && continue
144
+
145
+ repo_name=$(echo "$repo_json" | jq -r 'if type == "object" then (.name // empty) else . end' 2>/dev/null)
146
+ repo_path_rel=$(echo "$repo_json" | jq -r 'if type == "object" then (.path // empty) else empty end' 2>/dev/null)
147
+ [ -z "$repo_name" ] || [ "$repo_name" = "null" ] && continue
148
+
149
+ if [ -n "$repo_path_rel" ] && [ "$repo_path_rel" != "null" ]; then
150
+ repo_path="$WORKSPACE_DIR/$repo_path_rel"
151
+ else
152
+ repo_path="$WORKSPACE_DIR/$repo_name"
153
+ fi
154
+
155
+ process_repo_entry "$repo_name" "$repo_path"
156
+ done
157
+ fi
158
+ else
159
+ for repo_path in "$WORKSPACE_DIR"/*; do
160
+ if [ -d "$repo_path/.git" ]; then
161
+ repo_name=$(basename "$repo_path")
162
+ process_repo_entry "$repo_name" "$repo_path"
163
+ fi
164
+ done
165
+ fi
166
+
167
+ # Collect repo results into one array
168
+ repos_json="["
169
+ first=true
170
+ for f in "$temp_dir"/repo_*.json; do
171
+ [ -f "$f" ] && [ -s "$f" ] || continue
172
+ if $first; then
173
+ first=false
174
+ else
175
+ repos_json="$repos_json,"
176
+ fi
177
+ repos_json="$repos_json$(cat "$f")"
178
+ done
179
+ repos_json="$repos_json]"
180
+
181
+ if $overall_success; then
182
+ overall_success_json=true
183
+ else
184
+ overall_success_json=false
185
+ fi
186
+
187
+ jq -n \
188
+ --argjson target_turn "$TARGET_TURN" \
189
+ --argjson success "$overall_success_json" \
190
+ --argjson repos "$repos_json" \
191
+ '{
192
+ target_turn: $target_turn,
193
+ success: $success,
194
+ repos: $repos
195
+ }' > "$TASK_OUTPUT_DIR/rewind-result.json"
196
+
197
+ cat "$TASK_OUTPUT_DIR/rewind-result.json"
@@ -8,6 +8,7 @@ set -o pipefail
8
8
  TASK_OUTPUT_DIR="${TASK_OUTPUT_DIR:-/task-output}"
9
9
  WORKSPACE_DIR="${WORKSPACE_DIR:-/workspace}"
10
10
  patches_dir="$TASK_OUTPUT_DIR/patches"
11
+ TURN_NUMBER="${TURN_NUMBER:-}"
11
12
 
12
13
  # Simple log function
13
14
  log() {
@@ -152,9 +153,58 @@ for f in "$temp_dir"/repo_*.json; do
152
153
  done
153
154
  result="$result]"
154
155
 
155
- # Update task.json if it exists (single jq call for all stats)
156
+ # Optional per-turn snapshot copy for rewind history
157
+ turn_number_valid=false
158
+ if [[ "$TURN_NUMBER" =~ ^[0-9]+$ ]]; then
159
+ turn_number_valid=true
160
+ fi
161
+
162
+ if $turn_number_valid; then
163
+ turn_dir="$patches_dir/turn-$TURN_NUMBER"
164
+ mkdir -p "$turn_dir"
165
+ find "$turn_dir" -maxdepth 1 -type f -name '*.patch' -delete 2>/dev/null || true
166
+
167
+ while IFS= read -r patch_file; do
168
+ [ -z "$patch_file" ] && continue
169
+ [ -f "$patches_dir/$patch_file" ] || continue
170
+ cp "$patches_dir/$patch_file" "$turn_dir/$patch_file"
171
+ done < <(echo "$result" | jq -r '.[]?.patch_file' 2>/dev/null)
172
+ fi
173
+
174
+ # Update task.json with repos_changed and stats (create if it doesn't exist)
175
+ # This ensures sync results are readable by the server even for staged/running tasks
176
+ existing_json='{}'
156
177
  if [ -f "$TASK_OUTPUT_DIR/task.json" ]; then
157
- jq --argjson repos "$result" '
178
+ existing_json=$(cat "$TASK_OUTPUT_DIR/task.json")
179
+ fi
180
+
181
+ turn_timestamp=$(date -u +"%Y-%m-%dT%H:%M:%SZ")
182
+ if $turn_number_valid; then
183
+ echo "$existing_json" | jq --argjson repos "$result" --argjson turn "$TURN_NUMBER" --arg timestamp "$turn_timestamp" '
184
+ .repos_changed = $repos |
185
+ .stats = {
186
+ repositories: ($repos | length),
187
+ filesChanged: ([$repos[].files_changed] | add // 0),
188
+ linesAdded: ([$repos[].lines_added] | add // 0),
189
+ linesDeleted: ([$repos[].lines_deleted] | add // 0)
190
+ } |
191
+ .turn_patches = (
192
+ ((.turn_patches // []) | map(select((.turn // -1) != $turn))) + [{
193
+ turn: $turn,
194
+ repos_changed: ($repos | map(.name)),
195
+ stats: {
196
+ repositories: ($repos | length),
197
+ filesChanged: ([$repos[].files_changed] | add // 0),
198
+ linesAdded: ([$repos[].lines_added] | add // 0),
199
+ linesDeleted: ([$repos[].lines_deleted] | add // 0)
200
+ },
201
+ timestamp: $timestamp
202
+ }] | sort_by(.turn)
203
+ )
204
+ ' > "$TASK_OUTPUT_DIR/task.json.tmp" && \
205
+ mv "$TASK_OUTPUT_DIR/task.json.tmp" "$TASK_OUTPUT_DIR/task.json"
206
+ else
207
+ echo "$existing_json" | jq --argjson repos "$result" '
158
208
  .repos_changed = $repos |
159
209
  .stats = {
160
210
  repositories: ($repos | length),
@@ -162,7 +212,7 @@ if [ -f "$TASK_OUTPUT_DIR/task.json" ]; then
162
212
  linesAdded: ([$repos[].lines_added] | add // 0),
163
213
  linesDeleted: ([$repos[].lines_deleted] | add // 0)
164
214
  }
165
- ' "$TASK_OUTPUT_DIR/task.json" > "$TASK_OUTPUT_DIR/task.json.tmp" && \
215
+ ' > "$TASK_OUTPUT_DIR/task.json.tmp" && \
166
216
  mv "$TASK_OUTPUT_DIR/task.json.tmp" "$TASK_OUTPUT_DIR/task.json"
167
217
  fi
168
218