samagotchi 0.138.0 → 0.139.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.
- checksums.yaml +4 -4
- data/CHANGELOG.md +20 -1
- data/docs/architecture.md +33 -7
- data/docs/cli.md +5 -1
- data/docs/configuration.md +57 -4
- data/docs/internals/background-tasks.md +2 -2
- data/docs/internals/engine.md +2 -1
- data/docs/internals/tool-guardrails.md +1 -1
- data/docs/internals/web-push.md +20 -0
- data/docs/sessions.md +2 -1
- data/lib/samagotchi/bundles/system/manifest.yml +1 -1
- data/lib/samagotchi/config.rb +4 -0
- data/lib/samagotchi/engine.rb +4 -2
- data/lib/samagotchi/engine_peers.rb +2 -1
- data/lib/samagotchi/reply_wait/poll.rb +9 -2
- data/lib/samagotchi/reply_wait.rb +9 -3
- data/lib/samagotchi/task_finish_note.rb +8 -5
- data/lib/samagotchi/task_watch.rb +11 -6
- data/lib/samagotchi/tool_activity.rb +3 -3
- data/lib/samagotchi/tool_declarations.rb +2 -2
- data/lib/samagotchi/tools/builtins.rb +2 -1
- data/lib/samagotchi/tools/delegate_wait.rb +23 -2
- data/lib/samagotchi/tools/execute.rb +51 -9
- data/lib/samagotchi/tools/task_runtime.rb +1 -1
- data/lib/samagotchi/version.rb +1 -1
- data/lib/samagotchi/web/app.rb +30 -158
- data/lib/samagotchi/web/auto_update.rb +282 -0
- data/lib/samagotchi/web/public/app.js +3 -3
- data/lib/samagotchi/web/public/task_stop.js +3 -3
- data/lib/samagotchi/web/public/update_notice.js +10 -4
- data/lib/samagotchi/web/public/update_sheet.js +3 -2
- data/lib/samagotchi/web/push/notifier.rb +66 -5
- data/lib/samagotchi/web/server.rb +45 -1
- data/lib/samagotchi/web/session_access_routes.rb +1 -1
- data/lib/samagotchi/web/session_hub.rb +20 -3
- data/lib/samagotchi/web/update_check.rb +56 -10
- data/lib/samagotchi/web/update_ops.rb +240 -0
- data/lib/samagotchi/worker_task_wakes.rb +1 -1
- metadata +3 -1
|
@@ -17,13 +17,22 @@ module Samagotchi
|
|
|
17
17
|
module Web
|
|
18
18
|
# Checks RubyGems away from the session hub's Monitor and publishes the
|
|
19
19
|
# result as chi web's version state. The state file preserves cooldowns
|
|
20
|
-
# and version-level notice deduplication across chi web restarts
|
|
20
|
+
# and version-level notice deduplication across chi web restarts, and
|
|
21
|
+
# keeps AutoUpdate's record (the run in progress, the versions it gave
|
|
22
|
+
# up on) beside them.
|
|
23
|
+
#
|
|
24
|
+
# It runs when update.notify is on, or when update.auto is install
|
|
25
|
+
# (AutoUpdate needs the answer): with notify off the page and the phone
|
|
26
|
+
# are told nothing, the hub's `available` stays nil.
|
|
21
27
|
class UpdateCheck
|
|
22
28
|
FIRST_CHECK = 30
|
|
23
29
|
# A failed check (offline at start) tries again this soon, not a whole check_hours later.
|
|
24
30
|
RETRY = 3600
|
|
25
31
|
|
|
32
|
+
# @param enabled [Boolean] update.notify: the answer goes to the page
|
|
33
|
+
# @param auto [Boolean] update.auto is install: check even when +enabled+ is not
|
|
26
34
|
def initialize(hub:, enabled: Config.get("update.notify") == "on",
|
|
35
|
+
auto: Config.get("update.auto") == "install",
|
|
27
36
|
check_hours: Config.get("update.check_hours"),
|
|
28
37
|
min_age_hours: Config.get("update.min_age_hours"),
|
|
29
38
|
fetcher: -> { GemUpdate.new.releases }, clock: -> { Time.now },
|
|
@@ -31,7 +40,8 @@ module Samagotchi
|
|
|
31
40
|
gem_spec: InstalledGem.spec, bundler: nil, gem_dir: ChiHome.gem_dir(gem_spec: gem_spec),
|
|
32
41
|
installed_versions: InstalledVersions.new)
|
|
33
42
|
@hub = hub
|
|
34
|
-
@
|
|
43
|
+
@notify = enabled
|
|
44
|
+
@enabled = enabled || auto
|
|
35
45
|
@check_seconds = [check_hours.to_i, 1].max * 3600
|
|
36
46
|
@min_age_seconds = [min_age_hours.to_i, 0].max * 3600
|
|
37
47
|
@fetcher = fetcher
|
|
@@ -55,7 +65,7 @@ module Samagotchi
|
|
|
55
65
|
return self if @thread&.alive?
|
|
56
66
|
|
|
57
67
|
@stopped = false
|
|
58
|
-
@hub.update_available(available: still_newer(@state[:available]), blocked: @blocked)
|
|
68
|
+
@hub.update_available(available: @notify ? still_newer(@state[:available]) : nil, blocked: @blocked)
|
|
59
69
|
@thread = Thread.new { run_loop }
|
|
60
70
|
self
|
|
61
71
|
end
|
|
@@ -80,11 +90,11 @@ module Samagotchi
|
|
|
80
90
|
releases = @fetcher.call
|
|
81
91
|
available = available_release(releases, now)&.version
|
|
82
92
|
notified = @state[:notified]
|
|
83
|
-
notify = available && !notified.include?(available)
|
|
84
|
-
next_state =
|
|
85
|
-
|
|
93
|
+
notify = @notify && available && !notified.include?(available)
|
|
94
|
+
next_state = @state.merge(checked_at: now.iso8601, available: available,
|
|
95
|
+
notified: notify ? (notified + [available]) : notified)
|
|
86
96
|
persist(next_state)
|
|
87
|
-
@hub.update_available(available: available, blocked: @blocked, notify: !!notify)
|
|
97
|
+
@hub.update_available(available: @notify ? available : nil, blocked: @blocked, notify: !!notify)
|
|
88
98
|
@failed = false
|
|
89
99
|
true
|
|
90
100
|
rescue StandardError => e
|
|
@@ -94,6 +104,36 @@ module Samagotchi
|
|
|
94
104
|
end
|
|
95
105
|
end
|
|
96
106
|
|
|
107
|
+
# The release the last check found and this chi (and the installed
|
|
108
|
+
# ones) are still behind, whether or not the page was told.
|
|
109
|
+
# @return [String, nil]
|
|
110
|
+
def available
|
|
111
|
+
still_newer(@state[:available])
|
|
112
|
+
end
|
|
113
|
+
|
|
114
|
+
# AutoUpdate's record of the run it started: {version:, from:, phase:,
|
|
115
|
+
# kept:, workers:, pushed:, detail:} (symbol keys), nil when none.
|
|
116
|
+
# @return [Hash, nil]
|
|
117
|
+
def auto_state
|
|
118
|
+
@checking.synchronize { @state[:auto]&.dup }
|
|
119
|
+
end
|
|
120
|
+
|
|
121
|
+
# Whether AutoUpdate already tried +version+ and failed (one automatic
|
|
122
|
+
# try per version).
|
|
123
|
+
def auto_failed?(version)
|
|
124
|
+
@checking.synchronize { @state[:auto_failed].include?(version) }
|
|
125
|
+
end
|
|
126
|
+
|
|
127
|
+
# Saves AutoUpdate's record (nil clears it); +failed+ adds a version to
|
|
128
|
+
# the ones it gave up on.
|
|
129
|
+
def save_auto(auto, failed: nil)
|
|
130
|
+
@checking.synchronize do
|
|
131
|
+
failed_list = failed && !@state[:auto_failed].include?(failed) ? @state[:auto_failed] + [failed] : @state[:auto_failed]
|
|
132
|
+
persist(@state.merge(auto: auto, auto_failed: failed_list))
|
|
133
|
+
end
|
|
134
|
+
nil
|
|
135
|
+
end
|
|
136
|
+
|
|
97
137
|
# Why chi web can't install a gem here (the notice's and POST /api/update's
|
|
98
138
|
# one answer), or nil: a source checkout, a Bundler run, a gem dir it
|
|
99
139
|
# can't write.
|
|
@@ -158,19 +198,25 @@ module Samagotchi
|
|
|
158
198
|
def blocked_reason = self.class.blocked_reason(gem_spec: @gem_spec, bundler: @bundler, gem_dir: @gem_dir)
|
|
159
199
|
|
|
160
200
|
def load_state
|
|
161
|
-
return
|
|
201
|
+
return empty_state unless File.file?(@state_file)
|
|
162
202
|
|
|
163
203
|
data = JSON.parse(File.read(@state_file))
|
|
164
204
|
{
|
|
165
205
|
checked_at: data["checked_at"],
|
|
166
206
|
available: data["available"],
|
|
167
|
-
notified:
|
|
207
|
+
notified: strings(data["notified"]),
|
|
208
|
+
auto: data["auto"].is_a?(Hash) ? data["auto"].transform_keys(&:to_sym) : nil,
|
|
209
|
+
auto_failed: strings(data["auto_failed"])
|
|
168
210
|
}
|
|
169
211
|
rescue JSON::ParserError, SystemCallError => e
|
|
170
212
|
Log.warn(:web, "update_check_failed", error: e.class.name, msg: e.message)
|
|
171
|
-
|
|
213
|
+
empty_state
|
|
172
214
|
end
|
|
173
215
|
|
|
216
|
+
def empty_state = { checked_at: nil, available: nil, notified: [], auto: nil, auto_failed: [] }
|
|
217
|
+
|
|
218
|
+
def strings(list) = Array(list).select { |version| version.is_a?(String) }
|
|
219
|
+
|
|
174
220
|
def persist(state)
|
|
175
221
|
FileUtils.mkdir_p(File.dirname(@state_file))
|
|
176
222
|
AtomicFile.write(@state_file, JSON.generate(state), perm: 0o600)
|
|
@@ -0,0 +1,240 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
require "json"
|
|
4
|
+
require "open3"
|
|
5
|
+
require "rbconfig"
|
|
6
|
+
require_relative "../child_env"
|
|
7
|
+
require_relative "../chi_home"
|
|
8
|
+
require_relative "../client_id"
|
|
9
|
+
require_relative "../installed_gem"
|
|
10
|
+
require_relative "../live_versions"
|
|
11
|
+
require_relative "../log"
|
|
12
|
+
require_relative "../session"
|
|
13
|
+
require_relative "../session_manager"
|
|
14
|
+
require_relative "../version"
|
|
15
|
+
require_relative "update_check"
|
|
16
|
+
|
|
17
|
+
module Samagotchi
|
|
18
|
+
module Web
|
|
19
|
+
# The three update operations chi web runs for its owner, one at a time
|
|
20
|
+
# (App's routes ask for them, and so does AutoUpdate): an install
|
|
21
|
+
# (`chi update --to V` in a child), a pass restarting the stale workers,
|
|
22
|
+
# and chi web's own restart. Each answers at once, :started (it runs on
|
|
23
|
+
# its own thread, its progress and result going out as `chi_update`
|
|
24
|
+
# frames) or a refusal; the caller decides what a refusal says.
|
|
25
|
+
class UpdateOps
|
|
26
|
+
# chi update's own words when it printed no rows.
|
|
27
|
+
class UpdateFailed < StandardError; end
|
|
28
|
+
|
|
29
|
+
# @param hub [SessionHub, nil] the frames' publisher
|
|
30
|
+
# @param manager [#restart_session] SessionManager
|
|
31
|
+
# @param state_dir [String, nil] the sessions dir (nil: the default)
|
|
32
|
+
# @param runner [#call] `(argv, env:, progress:) → [stdout, success]`
|
|
33
|
+
# runs the chi update child
|
|
34
|
+
# @param blocked [#call] why this install can't take a gem, or nil
|
|
35
|
+
def initialize(hub:, manager: SessionManager, state_dir: nil, runner: nil, blocked: nil)
|
|
36
|
+
@hub = hub
|
|
37
|
+
@manager = manager
|
|
38
|
+
@state_dir = state_dir
|
|
39
|
+
@runner = runner || method(:run_update_command)
|
|
40
|
+
@blocked = blocked || -> { UpdateCheck.blocked_reason }
|
|
41
|
+
@mutex = Mutex.new
|
|
42
|
+
@running = false
|
|
43
|
+
@restart_web = nil
|
|
44
|
+
end
|
|
45
|
+
|
|
46
|
+
# Set by Server.start: the lambda that has chi web restart itself
|
|
47
|
+
# (true; :no_wrapper when there is nothing to exec; false when one was
|
|
48
|
+
# already asked for).
|
|
49
|
+
attr_writer :restart_web
|
|
50
|
+
|
|
51
|
+
# Whether the restart lambda is there (a stand-in server has none).
|
|
52
|
+
def restart_web? = !@restart_web.nil?
|
|
53
|
+
|
|
54
|
+
# Why a gem can't be installed here (UpdateCheck.blocked_reason), or nil.
|
|
55
|
+
def blocked_reason = @blocked.call
|
|
56
|
+
|
|
57
|
+
# Whether an operation runs (or chi web is restarting).
|
|
58
|
+
def running? = @mutex.synchronize { @running }
|
|
59
|
+
|
|
60
|
+
# Runs `chi update --to +version+` on a thread.
|
|
61
|
+
# @param wait [Boolean] block until it ends and answer its final
|
|
62
|
+
# `{state:, rows:}` instead of :started
|
|
63
|
+
# @return [Symbol, Hash] :started, :busy (another operation runs) or
|
|
64
|
+
# :start_failed (no thread); with wait: the result
|
|
65
|
+
def install(version, wait: false, auto: false)
|
|
66
|
+
return :busy unless claim
|
|
67
|
+
|
|
68
|
+
@hub.publish_update(state: "running", rows: [], operation: "install", version: version, auto: auto)
|
|
69
|
+
thread = Thread.new { perform_install(version, auto) }
|
|
70
|
+
wait ? thread.value : :started
|
|
71
|
+
rescue ThreadError => e
|
|
72
|
+
release
|
|
73
|
+
Log.warn(:web, "update_start_failed", error: e.class.name, msg: e.message)
|
|
74
|
+
@hub&.publish_update(state: "failed", rows: [
|
|
75
|
+
{ "component" => "chi update", "status" => "failed (couldn't start update)", "failed" => true, "kept" => [] }
|
|
76
|
+
], operation: "install", auto: auto)
|
|
77
|
+
:start_failed
|
|
78
|
+
end
|
|
79
|
+
|
|
80
|
+
# Restarts every stale worker (the worker refuses atomically when busy).
|
|
81
|
+
# @param select [#call, nil] asked of each stale worker (a
|
|
82
|
+
# LiveVersions::Worker): false leaves it, as a `skipped` row
|
|
83
|
+
# @return [Symbol, Hash] as #install
|
|
84
|
+
def restart_workers(wait: false, auto: false, select: nil)
|
|
85
|
+
return :busy unless claim
|
|
86
|
+
|
|
87
|
+
@hub.publish_update(state: "running", rows: [], operation: "workers", auto: auto)
|
|
88
|
+
thread = Thread.new { perform_worker_restarts(auto, select) }
|
|
89
|
+
wait ? thread.value : :started
|
|
90
|
+
rescue ThreadError => e
|
|
91
|
+
release
|
|
92
|
+
Log.warn(:web, "worker_restart_start_failed", error: e.class.name, msg: e.message)
|
|
93
|
+
@hub&.publish_update(state: "failed", rows: [
|
|
94
|
+
{ "state" => "failed", "reason" => "couldn't start worker restarts" }
|
|
95
|
+
], operation: "workers", auto: auto)
|
|
96
|
+
:start_failed
|
|
97
|
+
end
|
|
98
|
+
|
|
99
|
+
# The exec replaces this process: an update's chi update child (its
|
|
100
|
+
# pipes read here) or a worker restart pass would die mid-way, so
|
|
101
|
+
# neither may run, and none starts once the restart is asked for.
|
|
102
|
+
# @return [Symbol] :unavailable (no restart lambda), :busy, :started,
|
|
103
|
+
# :no_wrapper or :requested (already restarting)
|
|
104
|
+
def restart_web
|
|
105
|
+
return :unavailable unless @restart_web
|
|
106
|
+
|
|
107
|
+
busy = @mutex.synchronize do
|
|
108
|
+
next true if @running
|
|
109
|
+
|
|
110
|
+
@running = true # held for good once restarting: the exec ends this process
|
|
111
|
+
false
|
|
112
|
+
end
|
|
113
|
+
return :busy if busy
|
|
114
|
+
|
|
115
|
+
case @restart_web.call
|
|
116
|
+
when true then return :started
|
|
117
|
+
when :no_wrapper then answer = :no_wrapper
|
|
118
|
+
else answer = :requested
|
|
119
|
+
end
|
|
120
|
+
release
|
|
121
|
+
answer
|
|
122
|
+
end
|
|
123
|
+
|
|
124
|
+
# The chi update child: with a chi home, the home's chi (its gem dirs
|
|
125
|
+
# are what new workers activate); else this Ruby and the wrapper.
|
|
126
|
+
def update_argv(version, home: ChiHome.current)
|
|
127
|
+
args = ["update", "--to", version, "--no-desktop", "--json"]
|
|
128
|
+
return home.boot_argv(run: ChiHome::CHI_BOOT) + ["--", *args] if home
|
|
129
|
+
|
|
130
|
+
[RbConfig.ruby, InstalledGem.wrapper, *args]
|
|
131
|
+
end
|
|
132
|
+
|
|
133
|
+
private
|
|
134
|
+
|
|
135
|
+
def claim
|
|
136
|
+
@mutex.synchronize do
|
|
137
|
+
next false if @running
|
|
138
|
+
|
|
139
|
+
@running = true
|
|
140
|
+
end
|
|
141
|
+
end
|
|
142
|
+
|
|
143
|
+
def release
|
|
144
|
+
@mutex.synchronize { @running = false }
|
|
145
|
+
end
|
|
146
|
+
|
|
147
|
+
def perform_install(version, auto)
|
|
148
|
+
argv = update_argv(version)
|
|
149
|
+
said = nil
|
|
150
|
+
output, success = @runner.call(argv, env: ChildEnv.unbundled,
|
|
151
|
+
progress: lambda { |line|
|
|
152
|
+
said = line unless line.strip.empty?
|
|
153
|
+
@hub.publish_update(state: "running", rows: [], progress: line,
|
|
154
|
+
operation: "install", version: version, auto: auto)
|
|
155
|
+
})
|
|
156
|
+
rows = update_rows(output)
|
|
157
|
+
# No rows (a usage error, a refusal): what the child said is the reason.
|
|
158
|
+
raise UpdateFailed, said || output.lines.map(&:strip).find { |line| !line.empty? } || "chi update printed no rows" unless rows
|
|
159
|
+
|
|
160
|
+
# The installed version as of now (the hub's own look is up to a probe interval old), before "done".
|
|
161
|
+
@hub.refresh_installed if success
|
|
162
|
+
state = success ? "done" : "failed"
|
|
163
|
+
@hub.publish_update(state: state, rows: rows, operation: "install", version: version, auto: auto)
|
|
164
|
+
{ state: state, rows: rows }
|
|
165
|
+
rescue StandardError => e
|
|
166
|
+
Log.exception(:web, "update_failed", e)
|
|
167
|
+
rows = [{ "component" => "chi update", "status" => "failed (#{e.message.lines.first.to_s.strip})",
|
|
168
|
+
"failed" => true, "kept" => [] }]
|
|
169
|
+
@hub.publish_update(state: "failed", rows: rows, operation: "install", version: version, auto: auto)
|
|
170
|
+
{ state: "failed", rows: rows }
|
|
171
|
+
ensure
|
|
172
|
+
release
|
|
173
|
+
end
|
|
174
|
+
|
|
175
|
+
# chi update --json's rows, or nil when it printed none.
|
|
176
|
+
def update_rows(output)
|
|
177
|
+
rows = JSON.parse(output)
|
|
178
|
+
rows if rows.is_a?(Array) && rows.all?(Hash)
|
|
179
|
+
rescue JSON::ParserError
|
|
180
|
+
nil
|
|
181
|
+
end
|
|
182
|
+
|
|
183
|
+
def run_update_command(argv, env:, progress:)
|
|
184
|
+
Open3.popen3(env, *argv) do |stdin, stdout, stderr, wait_thread|
|
|
185
|
+
stdin.close
|
|
186
|
+
stderr_thread = Thread.new { stderr.each_line { |line| progress.call(line.chomp) } }
|
|
187
|
+
output = stdout.read
|
|
188
|
+
stderr_thread.value
|
|
189
|
+
[output, wait_thread.value.success?]
|
|
190
|
+
end
|
|
191
|
+
end
|
|
192
|
+
|
|
193
|
+
# One worker's restart as its row.
|
|
194
|
+
def restart_one(worker)
|
|
195
|
+
restarted = @manager.restart_session(worker.session_id, state_dir: @state_dir, client_id: ClientId::WEB_RESTART)
|
|
196
|
+
@hub.touch(worker.session_id)
|
|
197
|
+
{ "session_id" => restarted.session_id, "state" => "restarted", "from_version" => restarted.from_version,
|
|
198
|
+
"version" => restarted.version }
|
|
199
|
+
rescue SessionManager::RestartRefused => e
|
|
200
|
+
# detail: the refusal's own words (RestartRefused.words), the reason code for clients.
|
|
201
|
+
{ "session_id" => worker.session_id, "state" => "refused", "reason" => e.reason.to_s,
|
|
202
|
+
"detail" => e.message, "holds" => e.holds.map(&:to_s) }
|
|
203
|
+
rescue SessionManager::OwnedByTUI => e
|
|
204
|
+
{ "session_id" => worker.session_id, "state" => "owned_by_tui", "detail" => e.message }
|
|
205
|
+
rescue StandardError => e
|
|
206
|
+
Log.exception(:web, "worker_restart_failed", e, sid: worker.session_id)
|
|
207
|
+
{ "session_id" => worker.session_id, "state" => "failed", "reason" => e.class.name, "detail" => e.message }
|
|
208
|
+
end
|
|
209
|
+
|
|
210
|
+
# A worker the caller's +select+ left: still on the old chi, to restart when it is idle.
|
|
211
|
+
def skipped(worker)
|
|
212
|
+
{ "session_id" => worker.session_id, "state" => "skipped", "reason" => "not_idle",
|
|
213
|
+
"detail" => "not idle for long enough: it restarts when it is idle" }
|
|
214
|
+
end
|
|
215
|
+
|
|
216
|
+
def default_state_dir = @state_dir || Session.default_state_dir
|
|
217
|
+
|
|
218
|
+
def perform_worker_restarts(auto, select)
|
|
219
|
+
# Installed now: the hub's own look is up to a full probe old (nil right after chi web started).
|
|
220
|
+
target = @hub.refresh_installed || VERSION
|
|
221
|
+
rows = []
|
|
222
|
+
LiveVersions.stale_workers(target, state_dir: default_state_dir).each do |worker|
|
|
223
|
+
row = select && !select.call(worker) ? skipped(worker) : restart_one(worker)
|
|
224
|
+
rows << row
|
|
225
|
+
@hub.publish_update(state: "running", rows: rows.dup, operation: "workers", auto: auto)
|
|
226
|
+
end
|
|
227
|
+
state = rows.any? { |row| row["state"] == "failed" } ? "failed" : "done"
|
|
228
|
+
@hub.publish_update(state: state, rows: rows, operation: "workers", auto: auto)
|
|
229
|
+
{ state: state, rows: rows }
|
|
230
|
+
rescue StandardError => e
|
|
231
|
+
Log.exception(:web, "worker_restart_failed", e)
|
|
232
|
+
rows = [{ "state" => "failed", "reason" => e.class.name, "detail" => e.message }]
|
|
233
|
+
@hub.publish_update(state: "failed", rows: rows, operation: "workers", auto: auto)
|
|
234
|
+
{ state: "failed", rows: rows }
|
|
235
|
+
ensure
|
|
236
|
+
release
|
|
237
|
+
end
|
|
238
|
+
end
|
|
239
|
+
end
|
|
240
|
+
end
|
|
@@ -129,7 +129,7 @@ module Samagotchi
|
|
|
129
129
|
end
|
|
130
130
|
|
|
131
131
|
def note(entry, record, wake:, turn_id: nil)
|
|
132
|
-
TaskFinishNote.note(record, limit_sec: entry.limit_sec, wake: wake, turn_id: turn_id,
|
|
132
|
+
TaskFinishNote.note(record, limit_sec: entry.limit_sec, early_sec: entry.early_sec, wake: wake, turn_id: turn_id,
|
|
133
133
|
cap: KernelLoop.resolve_output_char_cap(nil))
|
|
134
134
|
end
|
|
135
135
|
|
metadata
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
|
2
2
|
name: samagotchi
|
|
3
3
|
version: !ruby/object:Gem::Version
|
|
4
|
-
version: 0.
|
|
4
|
+
version: 0.139.0
|
|
5
5
|
platform: ruby
|
|
6
6
|
authors:
|
|
7
7
|
- Dmitry Dedov
|
|
@@ -752,6 +752,7 @@ files:
|
|
|
752
752
|
- lib/samagotchi/vision_support.rb
|
|
753
753
|
- lib/samagotchi/waiting_steer.rb
|
|
754
754
|
- lib/samagotchi/web/app.rb
|
|
755
|
+
- lib/samagotchi/web/auto_update.rb
|
|
755
756
|
- lib/samagotchi/web/capabilities.rb
|
|
756
757
|
- lib/samagotchi/web/editor.rb
|
|
757
758
|
- lib/samagotchi/web/grants.rb
|
|
@@ -951,6 +952,7 @@ files:
|
|
|
951
952
|
- lib/samagotchi/web/tunnel.rb
|
|
952
953
|
- lib/samagotchi/web/tunnel/tailscale.rb
|
|
953
954
|
- lib/samagotchi/web/update_check.rb
|
|
955
|
+
- lib/samagotchi/web/update_ops.rb
|
|
954
956
|
- lib/samagotchi/web/verify_fresh_routes.rb
|
|
955
957
|
- lib/samagotchi/worker.rb
|
|
956
958
|
- lib/samagotchi/worker_idle_exit.rb
|