ecoportal-api-v2 3.3.1 → 3.3.3

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.
data/CLAUDE.md ADDED
@@ -0,0 +1,82 @@
1
+ # CLAUDE.md — ecoportal-api-v2
2
+
3
+ AI agent instructions for this repository.
4
+
5
+ **Cross-cutting architecture context lives in `ecoportal-api-graphql` — see its `CLAUDE.md` and `.claude/` folder for the full dependency map, project history, and shared skills.**
6
+
7
+ ---
8
+
9
+ ## Repository Role
10
+
11
+ `ecoportal-api-v2` is the **REST API v2 gem**, extending `ecoportal-api` with a rich content model layer (pages, registers, people, S3 file handling). It provides the `Ecoportal::API::V2` and `Ecoportal::API::Common::Content` namespaces.
12
+
13
+ **Position in chain:**
14
+ ```
15
+ ecoportal-api
16
+
17
+ ecoportal-api-v2 ← THIS REPO
18
+
19
+ ecoportal-api-graphql
20
+
21
+ eco-helpers
22
+ ```
23
+
24
+ **Remote:** https://gitlab.ecoportal.co.nz/oscar/ecoportal-api-v2.git
25
+
26
+ ---
27
+
28
+ ## Key Folder Layout
29
+
30
+ ```
31
+ lib/ecoportal/api/
32
+ common/
33
+ concerns/ Benchmarkable, Threadable mixins
34
+ content/ Core model infrastructure (see below)
35
+ v2/
36
+ page/ Page model — sections, stages, components, forces, permissions
37
+ page/component/ All field types (text, number, date, selection, files, geo, etc.)
38
+ pages/ Pages collection, stage results, tasks
39
+ registers/ Register models — search results, stage/page results
40
+ people/ V2 people endpoint
41
+ s3/ S3 file upload/download infrastructure
42
+ ```
43
+
44
+ ### `Common::Content` — the model engine
45
+
46
+ The most complex part of this gem. Key classes:
47
+
48
+ - `DoubleModel` — two-layer model (a "live" doc and a "patch" doc) with attribute DSL, diffing, nesting, and change tracking. Almost all V2 models inherit from this.
49
+ - `CollectionModel` — ordered collection with upsert/delete/mutation support.
50
+ - `ClassHelpers` — `class_resolver` macro for injectable class references (pattern shared with `ecoportal-api-graphql`).
51
+ - `DocHelpers`, `ModelHelpers`, `IncluderHelpers` — utility mixins for model construction.
52
+
53
+ ---
54
+
55
+ ## Namespace
56
+
57
+ - `Ecoportal::API::V2` — v2 REST resources
58
+ - `Ecoportal::API::Common::Content` — shared model infrastructure (consumed downstream)
59
+ - `Ecoportal::API::Common::Concerns` — threading and benchmarking mixins
60
+
61
+ ---
62
+
63
+ ## Key Concerns
64
+
65
+ - `DoubleModel` is the foundational abstraction — understand it before modifying any V2 model.
66
+ - `Common::Content::ClassHelpers` mirrors the `ClassHelpers` in `ecoportal-api-graphql` — changes to one may need mirroring in the other.
67
+ - S3 upload is async with polling (`s3/files/poll.rb`) — timing-sensitive code.
68
+
69
+ ---
70
+
71
+ ## Backwards Compatibility
72
+
73
+ `eco-helpers` and `ecoportal-api-graphql` both depend on this gem. Changes to `Common::Content::*` or `V2::Page::*` interfaces must be checked against usage in both downstream gems before committing.
74
+
75
+ ---
76
+
77
+ ## Running Tests
78
+
79
+ ```bash
80
+ bundle install
81
+ bundle exec rspec
82
+ ```
@@ -33,7 +33,7 @@ Gem::Specification.new do |spec|
33
33
  spec.add_development_dependency 'rubocop-rake', '~> 0'
34
34
  spec.add_development_dependency 'yard', '~> 0.9'
35
35
 
36
- spec.add_dependency 'ecoportal-api', '~> 0.10', '>= 0.10.12'
36
+ spec.add_dependency 'ecoportal-api', '~> 0.10', '>= 0.10.16'
37
37
  spec.add_dependency 'mime-types', '~> 3.5', '>= 3.5.2'
38
38
  end
39
39
 
@@ -16,6 +16,7 @@ module Ecoportal
16
16
 
17
17
  def benchmarking(ref = nil, units: 1, print: false, &block)
18
18
  return yield unless benchmark_enabled?
19
+
19
20
  benchmark_mem(ref, units: units, print: false) do
20
21
  benchmark_time(ref, units: units, print: false, &block)
21
22
  end.tap do
@@ -178,7 +178,7 @@ module Ecoportal::API::Common::Content::DoubleModel::Attributable::Nesting
178
178
  nested_attrs: _cascaded_attributes.keys,
179
179
  attributes: {}
180
180
  }.merge(trace || {}).tap do |out_trace|
181
- _cascaded_attributes.each.with_object(out_trace) do |out, (attribute, obj_k)|
181
+ _cascaded_attributes.each.with_object(out_trace) do |(attribute, obj_k), out|
182
182
  next unless (obj = send(attribute))
183
183
 
184
184
  obj_k_path = key_path.dup.push(obj_k)
@@ -35,6 +35,7 @@ module Ecoportal
35
35
  def as_json
36
36
  doc
37
37
  end
38
+ alias_method :to_h, :as_json
38
39
 
39
40
  def print_pretty
40
41
  puts JSON.pretty_generate(as_json)
@@ -3,7 +3,9 @@ module Ecoportal
3
3
  class V2
4
4
  class S3
5
5
  class Files
6
- # Class service to upload multiple files in one go
6
+ # Class service to upload multiple files in one go.
7
+ # Full cycle: from local files to the org file manager.
8
+ # @note it uploads to S3, and converts it into a file container as well.
7
9
  class BatchUpload
8
10
  include Ecoportal::API::Common::Concerns::Benchmarkable
9
11
  include Ecoportal::API::Common::Concerns::Threadable
@@ -1,5 +1,5 @@
1
1
  module Ecoportal
2
2
  module API
3
- GEM2_VERSION = '3.3.1'.freeze
3
+ GEM2_VERSION = '3.3.3'.freeze
4
4
  end
5
5
  end
@@ -0,0 +1,307 @@
1
+ #!/usr/bin/env bash
2
+ # auto-worker-scheduler.sh
3
+ # Scheduler for auto-todo-worker. Called by the Claude Code Stop hook.
4
+ # Reads local config, checks token budget and conflict guard, then
5
+ # launches the worker as a background process if all gates pass.
6
+ #
7
+ # Log format: TIMESTAMP | ACTION | REASON
8
+ # Log file: .ai-assistance/local/auto-worker-changes.log
9
+ #
10
+ # Usage: bash scripts/auto-worker-scheduler.sh
11
+ # (called automatically by Stop hook — no arguments)
12
+ #
13
+ # Requires: jq (for JSON parsing). Falls back to grep/sed on missing jq.
14
+
15
+ set -euo pipefail
16
+
17
+ # ---------------------------------------------------------------------------
18
+ # Paths (all relative — script must be run from repo root)
19
+ # ---------------------------------------------------------------------------
20
+ TOKEN_BUDGET_JSON=".ai-assistance/token-budget.json"
21
+ LAST_RUN_JSON=".ai-assistance/local/auto-worker-last-run.json"
22
+ SESSION_LOCK=".ai-assistance/local/session.lock"
23
+ CHANGES_LOG=".ai-assistance/local/auto-worker-changes.log"
24
+
25
+ # ---------------------------------------------------------------------------
26
+ # Helpers
27
+ # ---------------------------------------------------------------------------
28
+
29
+ # ai_log_usage <component> <action> <detail> [session_id]
30
+ # Inline copy — scheduler is self-contained; common.sh is not sourced here.
31
+ # Never fails fatally; all errors suppressed.
32
+ ai_log_usage() {
33
+ local component="${1:-unknown}"
34
+ local action="${2:-run}"
35
+ local detail="${3:-}"
36
+ local session_id="${4:-}"
37
+ local log_dir=".ai-assistance/local/kpi"
38
+ local week
39
+ week=$(date +%Y-W%V 2>/dev/null || echo "unknown")
40
+ local log_file="${log_dir}/usage-${week}.jsonl"
41
+ local ts
42
+ ts=$(date -u +%Y-%m-%dT%H:%M:%SZ 2>/dev/null || echo "unknown")
43
+ mkdir -p "$log_dir" 2>/dev/null || true
44
+ printf '{"component":"%s","action":"%s","detail":"%s","ts":"%s","session_id":"%s"}\n' \
45
+ "$component" "$action" "$detail" "$ts" "$session_id" >> "$log_file" 2>/dev/null || true
46
+ }
47
+
48
+ log_action() {
49
+ local action="$1"
50
+ local reason="$2"
51
+ local ts
52
+ ts=$(date -u +"%Y-%m-%dT%H:%M:%SZ" 2>/dev/null || date +"%Y-%m-%dT%H:%M:%SZ")
53
+ mkdir -p "$(dirname "$CHANGES_LOG")"
54
+ printf "%s | %s | %s\n" "$ts" "$action" "$reason" >> "$CHANGES_LOG"
55
+ }
56
+
57
+ # Read a JSON field using jq if available, otherwise grep+sed fallback.
58
+ # Usage: json_field <file> <dotted.key> e.g. json_field config.json auto_worker.enabled
59
+ json_field() {
60
+ local file="$1"
61
+ local key="$2"
62
+ if command -v jq >/dev/null 2>&1; then
63
+ jq -r ".${key} // empty" "$file" 2>/dev/null
64
+ else
65
+ # Minimal fallback: grep for the last segment key, extract bare value.
66
+ local leaf
67
+ leaf=$(printf '%s' "$key" | sed 's/.*\.//')
68
+ grep -o "\"${leaf}\"[[:space:]]*:[[:space:]]*[^,}]*" "$file" 2>/dev/null \
69
+ | tail -1 \
70
+ | sed 's/.*:[[:space:]]*//' \
71
+ | tr -d '"' \
72
+ | tr -d ' '
73
+ fi
74
+ }
75
+
76
+ # ---------------------------------------------------------------------------
77
+ # Gate 1: auto_worker.enabled
78
+ # ---------------------------------------------------------------------------
79
+
80
+ if [ ! -f "$TOKEN_BUDGET_JSON" ]; then
81
+ log_action "SKIP" "token-budget.json not found — auto_worker not configured"
82
+ ai_log_usage "script/auto-worker-scheduler" "skip" "auto_worker not enabled -- token-budget.json not found"
83
+ exit 0
84
+ fi
85
+
86
+ enabled=$(json_field "$TOKEN_BUDGET_JSON" "auto_worker.enabled")
87
+ if [ "$enabled" != "true" ]; then
88
+ log_action "SKIP" "auto_worker.enabled is false or absent in token-budget.json"
89
+ ai_log_usage "script/auto-worker-scheduler" "skip" "auto_worker not enabled"
90
+ exit 0
91
+ fi
92
+
93
+ # ---------------------------------------------------------------------------
94
+ # Gate 2: Conflict guard — check for active Claude session via session.lock
95
+ # ---------------------------------------------------------------------------
96
+
97
+ if [ -f "$SESSION_LOCK" ]; then
98
+ lock_content=""
99
+ if [ -r "$SESSION_LOCK" ]; then
100
+ lock_content=$(cat "$SESSION_LOCK" 2>/dev/null || true)
101
+ fi
102
+ log_action "SKIP" "Active Claude session detected (session.lock present: ${lock_content})"
103
+ ai_log_usage "script/auto-worker-scheduler" "skip" "session.lock present"
104
+ exit 0
105
+ fi
106
+
107
+ # ---------------------------------------------------------------------------
108
+ # Gate 3: Token budget threshold check
109
+ # ---------------------------------------------------------------------------
110
+ # Find the current ISO week KPI file. Format: weekly-YYYY-WNN.json
111
+ # We compute ISO week ourselves for portability.
112
+
113
+ iso_week() {
114
+ # Returns YYYY-WNN string for today (ISO 8601 week numbering)
115
+ if command -v python3 >/dev/null 2>&1; then
116
+ python3 -c "
117
+ import datetime
118
+ today = datetime.date.today()
119
+ iso = today.isocalendar()
120
+ print('%04d-W%02d' % (iso[0], iso[1]))
121
+ "
122
+ elif command -v python >/dev/null 2>&1; then
123
+ python -c "
124
+ import datetime
125
+ today = datetime.date.today()
126
+ iso = today.isocalendar()
127
+ print('%04d-W%02d' % (iso[0], iso[1]))
128
+ "
129
+ else
130
+ # Fallback: use date command Week-of-year (not strictly ISO but close enough)
131
+ date +"%Y-W%V" 2>/dev/null || date +"%Y-W%U"
132
+ fi
133
+ }
134
+
135
+ KPI_DIR=".ai-assistance/local/kpi"
136
+ WEEK_KEY=$(iso_week)
137
+ KPI_FILE="${KPI_DIR}/weekly-${WEEK_KEY}.json"
138
+
139
+ # Read quota and usage from KPI file (if present)
140
+ weekly_tokens_used=0
141
+ weekly_quota=0
142
+ window_start_ts="" # ISO timestamp of first session start in current 5-hour window
143
+
144
+ if [ -f "$KPI_FILE" ] && command -v jq >/dev/null 2>&1; then
145
+ # Sum tokens_used across all sessions
146
+ weekly_tokens_used=$(jq '[.sessions[]?.tokens_used // 0] | add // 0' "$KPI_FILE" 2>/dev/null || echo "0")
147
+ weekly_quota=$(jq -r '.quota // 0' "$KPI_FILE" 2>/dev/null || echo "0")
148
+
149
+ # Find most recent session start to compute reset window
150
+ window_start_ts=$(jq -r '
151
+ [.sessions[]?.session_start // empty] | sort | last // empty
152
+ ' "$KPI_FILE" 2>/dev/null || true)
153
+ fi
154
+
155
+ # Read quota from token-budget.json as fallback
156
+ if [ "$weekly_quota" = "0" ] || [ -z "$weekly_quota" ]; then
157
+ quota_from_config=$(json_field "$TOKEN_BUDGET_JSON" "weekly_quota.total_tokens")
158
+ if [ "$quota_from_config" != "null" ] && [ -n "$quota_from_config" ]; then
159
+ weekly_quota="$quota_from_config"
160
+ fi
161
+ fi
162
+
163
+ # If no quota is set (null), skip the threshold gate entirely
164
+ if [ "$weekly_quota" = "0" ] || [ "$weekly_quota" = "null" ] || [ -z "$weekly_quota" ]; then
165
+ log_action "OK" "No weekly quota set — skipping token threshold gate"
166
+ else
167
+ # Calculate usage percentage
168
+ if command -v python3 >/dev/null 2>&1; then
169
+ usage_pct=$(python3 -c "
170
+ used = float('${weekly_tokens_used}' or '0')
171
+ quota = float('${weekly_quota}')
172
+ print('%.1f' % (used / quota * 100) if quota > 0 else '0')
173
+ ")
174
+ else
175
+ usage_pct=0
176
+ fi
177
+
178
+ # Determine time remaining in current 5-hour reset window
179
+ # 5-hour window = 18000 seconds
180
+ window_seconds_elapsed=0
181
+ if [ -n "$window_start_ts" ] && command -v python3 >/dev/null 2>&1; then
182
+ window_seconds_elapsed=$(python3 -c "
183
+ import datetime, sys
184
+ try:
185
+ ts = '${window_start_ts}'.replace('Z', '+00:00')
186
+ start = datetime.datetime.fromisoformat(ts)
187
+ now = datetime.datetime.now(datetime.timezone.utc)
188
+ elapsed = (now - start).total_seconds()
189
+ print(int(max(0, elapsed)))
190
+ except Exception:
191
+ print(0)
192
+ ")
193
+ fi
194
+
195
+ WINDOW_DURATION=18000 # 5 hours in seconds
196
+ window_remaining=$(( WINDOW_DURATION - window_seconds_elapsed ))
197
+ if [ "$window_remaining" -lt 0 ]; then
198
+ window_remaining=0
199
+ fi
200
+ window_remaining_min=$(( window_remaining / 60 ))
201
+
202
+ # Determine threshold based on time remaining in window (ADR-007 table)
203
+ # >120 min -> 65%; 60-120 -> 75%; 45-60 -> 80%; 30-45 -> 90%; 15-30 -> 95%; <15 -> skip
204
+ if [ "$window_remaining_min" -lt 15 ]; then
205
+ log_action "SKIP" "Less than 15 min remaining in token reset window — will not launch"
206
+ ai_log_usage "script/auto-worker-scheduler" "skip" "token budget -- less than 15 min remaining in reset window"
207
+ exit 0
208
+ elif [ "$window_remaining_min" -lt 30 ]; then
209
+ threshold=95
210
+ elif [ "$window_remaining_min" -lt 45 ]; then
211
+ threshold=90
212
+ elif [ "$window_remaining_min" -lt 60 ]; then
213
+ threshold=80
214
+ elif [ "$window_remaining_min" -lt 120 ]; then
215
+ threshold=75
216
+ else
217
+ threshold=65
218
+ fi
219
+
220
+ # Compare usage_pct against threshold (integer comparison via python3)
221
+ if command -v python3 >/dev/null 2>&1; then
222
+ over_threshold=$(python3 -c "
223
+ pct = float('${usage_pct}' or '0')
224
+ thr = float('${threshold}')
225
+ print('true' if pct >= thr else 'false')
226
+ ")
227
+ else
228
+ over_threshold="false"
229
+ fi
230
+
231
+ if [ "$over_threshold" = "true" ]; then
232
+ log_action "SKIP" "Token usage ${usage_pct}% >= threshold ${threshold}% (${window_remaining_min} min remaining in window)"
233
+ ai_log_usage "script/auto-worker-scheduler" "skip" "token budget at ${usage_pct}% (threshold ${threshold}%)"
234
+ exit 0
235
+ fi
236
+
237
+ log_action "OK" "Token gate passed: ${usage_pct}% used, threshold ${threshold}% (${window_remaining_min} min remaining)"
238
+ fi
239
+
240
+ # ---------------------------------------------------------------------------
241
+ # Gate 4: Determine cadence (informational — actual cron cadence managed by
242
+ # CronCreate; this script just logs the recommendation)
243
+ # ---------------------------------------------------------------------------
244
+
245
+ recommended_cadence="2hr"
246
+ if [ -f "$LAST_RUN_JSON" ] && command -v jq >/dev/null 2>&1; then
247
+ completed_count=$(jq '.completed | length' "$LAST_RUN_JSON" 2>/dev/null || echo "0")
248
+ if [ "$completed_count" -gt "0" ]; then
249
+ recommended_cadence="30min"
250
+ fi
251
+ fi
252
+
253
+ # ---------------------------------------------------------------------------
254
+ # Gate 5: dangerously_skip_permissions opt-in
255
+ # ---------------------------------------------------------------------------
256
+
257
+ skip_perms=$(json_field "$TOKEN_BUDGET_JSON" "auto_worker.dangerously_skip_permissions")
258
+ AUTO_WORKER_PROMPT="Run the auto-worker for this repo.
259
+
260
+ Instructions:
261
+ 1. Read TODO.md and collect all items tagged \`[auto]\` or \`[auto:last]\`.
262
+ 2. If no \`[auto]\` items exist, exit cleanly with a brief log entry.
263
+ 3. Plan execution order as a dependency graph — do NOT follow TODO list order.
264
+ - Items without dependencies on each other may run in parallel (branch out when the work is self-contained enough to isolate).
265
+ - \`[auto:last]\` items run only after all \`[auto]\` items complete or are skipped.
266
+ 4. For each item:
267
+ - Execute the task.
268
+ - Mark it \`[x]\` in TODO.md on completion.
269
+ - Add any discovered follow-up tasks as new TODO items; tag \`[auto]\` only if they meet the eligibility criteria in standards/tooling/auto-worker-eligibility.md.
270
+ 5. Do NOT touch items that are not tagged \`[auto]\` or \`[auto:last]\`.
271
+ 6. At the end, write a one-paragraph summary of what was done, skipped, and added to TODO."
272
+
273
+ CLAUDE_FLAGS="--print \"${AUTO_WORKER_PROMPT}\""
274
+ if [ "$skip_perms" = "true" ]; then
275
+ CLAUDE_FLAGS="--dangerously-skip-permissions --print \"${AUTO_WORKER_PROMPT}\""
276
+ fi
277
+
278
+ # ---------------------------------------------------------------------------
279
+ # Launch: run auto-todo-worker as a background process
280
+ # ---------------------------------------------------------------------------
281
+
282
+ log_action "LAUNCH" "Launching auto-todo-worker (cadence recommendation: ${recommended_cadence})"
283
+ ai_log_usage "script/auto-worker-scheduler" "launch" "launching auto-todo-worker"
284
+
285
+ if [[ "${OSTYPE:-}" == "msys" ]] || [[ "${OSTYPE:-}" == "cygwin" ]] || \
286
+ [[ "${OS:-}" == "Windows_NT" ]]; then
287
+ # Windows via Git Bash / WSL
288
+ # Use PowerShell Start-Process to launch detached from the current terminal
289
+ powershell -Command "Start-Process -FilePath 'claude' \
290
+ -ArgumentList '${CLAUDE_FLAGS}' \
291
+ -WorkingDirectory '${PWD}' \
292
+ -NoNewWindow" &
293
+ launch_pid=$!
294
+ else
295
+ # Unix / macOS / WSL2
296
+ if [ "$skip_perms" = "true" ]; then
297
+ nohup claude --dangerously-skip-permissions --print "run auto worker" \
298
+ > ".ai-assistance/local/auto-worker-stdout.log" 2>&1 &
299
+ else
300
+ nohup claude --print "run auto worker" \
301
+ > ".ai-assistance/local/auto-worker-stdout.log" 2>&1 &
302
+ fi
303
+ launch_pid=$!
304
+ fi
305
+
306
+ log_action "LAUNCHED" "Background PID ${launch_pid:-unknown} — flags: ${CLAUDE_FLAGS}"
307
+ exit 0
metadata CHANGED
@@ -1,14 +1,13 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: ecoportal-api-v2
3
3
  version: !ruby/object:Gem::Version
4
- version: 3.3.1
4
+ version: 3.3.3
5
5
  platform: ruby
6
6
  authors:
7
7
  - Oscar Segura
8
- autorequire:
9
8
  bindir: exe
10
9
  cert_chain: []
11
- date: 2025-06-11 00:00:00.000000000 Z
10
+ date: 1980-01-02 00:00:00.000000000 Z
12
11
  dependencies:
13
12
  - !ruby/object:Gem::Dependency
14
13
  name: pry
@@ -135,7 +134,7 @@ dependencies:
135
134
  version: '0.10'
136
135
  - - ">="
137
136
  - !ruby/object:Gem::Version
138
- version: 0.10.12
137
+ version: 0.10.16
139
138
  type: :runtime
140
139
  prerelease: false
141
140
  version_requirements: !ruby/object:Gem::Requirement
@@ -145,7 +144,7 @@ dependencies:
145
144
  version: '0.10'
146
145
  - - ">="
147
146
  - !ruby/object:Gem::Version
148
- version: 0.10.12
147
+ version: 0.10.16
149
148
  - !ruby/object:Gem::Dependency
150
149
  name: mime-types
151
150
  requirement: !ruby/object:Gem::Requirement
@@ -166,13 +165,20 @@ dependencies:
166
165
  - - ">="
167
166
  - !ruby/object:Gem::Version
168
167
  version: 3.5.2
169
- description:
170
168
  email:
171
169
  - oscar@ecoportal.co.nz
172
170
  executables: []
173
171
  extensions: []
174
172
  extra_rdoc_files: []
175
173
  files:
174
+ - ".ai-assistance/code/double_model_patterns.md"
175
+ - ".ai-assistance/conventions/code-working-tree-protocol.md"
176
+ - ".ai-assistance/scripts/token-logger.js"
177
+ - ".ai-assistance/scripts/token-report.ts"
178
+ - ".ai-assistance/scripts/token-session-start.js"
179
+ - ".ai-assistance/skills/eP_AI_Manager/SKILL.md"
180
+ - ".ai-assistance/token-budget.json"
181
+ - ".claude/settings.json"
176
182
  - ".gitignore"
177
183
  - ".markdownlint.json"
178
184
  - ".rspec"
@@ -180,6 +186,7 @@ files:
180
186
  - ".ruby-version"
181
187
  - ".yardopts"
182
188
  - CHANGELOG.md
189
+ - CLAUDE.md
183
190
  - Gemfile
184
191
  - LICENSE
185
192
  - README.md
@@ -329,12 +336,12 @@ files:
329
336
  - lib/ecoportal/api/v2/s3/files/poll_status.rb
330
337
  - lib/ecoportal/api/v2/s3/upload.rb
331
338
  - lib/ecoportal/api/v2_version.rb
339
+ - scripts/auto-worker-scheduler.sh
332
340
  homepage: https://www.ecoportal.com
333
341
  licenses:
334
342
  - MIT
335
343
  metadata:
336
344
  rubygems_mfa_required: 'true'
337
- post_install_message:
338
345
  rdoc_options: []
339
346
  require_paths:
340
347
  - lib
@@ -349,8 +356,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
349
356
  - !ruby/object:Gem::Version
350
357
  version: '0'
351
358
  requirements: []
352
- rubygems_version: 3.5.23
353
- signing_key:
359
+ rubygems_version: 4.0.8
354
360
  specification_version: 4
355
361
  summary: A collection of helpers for interacting with the ecoPortal MS's V2 API
356
362
  test_files: []