openclacky 1.2.5 → 1.2.7
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 +43 -0
- data/README.md +34 -0
- data/README_CN.md +34 -0
- data/lib/clacky/agent/cost_tracker.rb +24 -10
- data/lib/clacky/agent/llm_caller.rb +25 -3
- data/lib/clacky/agent/message_compressor.rb +2 -1
- data/lib/clacky/agent/message_compressor_helper.rb +6 -2
- data/lib/clacky/agent/session_serializer.rb +23 -4
- data/lib/clacky/agent/tool_executor.rb +14 -0
- data/lib/clacky/agent/tool_registry.rb +0 -7
- data/lib/clacky/agent.rb +43 -10
- data/lib/clacky/agent_config.rb +54 -6
- data/lib/clacky/billing/billing_store.rb +62 -4
- data/lib/clacky/brand_config.rb +5 -0
- data/lib/clacky/cli.rb +76 -24
- data/lib/clacky/client.rb +59 -4
- data/lib/clacky/default_parsers/wps_parser.rb +82 -0
- data/lib/clacky/default_skills/onboard/SKILL.md +2 -2
- data/lib/clacky/json_ui_controller.rb +5 -2
- data/lib/clacky/message_format/anthropic.rb +13 -3
- data/lib/clacky/message_format/bedrock.rb +2 -2
- data/lib/clacky/plain_ui_controller.rb +1 -1
- data/lib/clacky/platform_http_client.rb +28 -1
- data/lib/clacky/providers.rb +11 -29
- data/lib/clacky/server/channel/channel_manager.rb +148 -12
- data/lib/clacky/server/channel/channel_ui_controller.rb +4 -2
- data/lib/clacky/server/http_server.rb +133 -13
- data/lib/clacky/server/session_registry.rb +30 -4
- data/lib/clacky/server/web_ui_controller.rb +6 -3
- data/lib/clacky/tools/browser.rb +4 -13
- data/lib/clacky/tools/terminal.rb +23 -27
- data/lib/clacky/ui2/ui_controller.rb +1 -1
- data/lib/clacky/ui_interface.rb +1 -1
- data/lib/clacky/utils/file_processor.rb +3 -0
- data/lib/clacky/utils/parser_manager.rb +3 -0
- data/lib/clacky/version.rb +1 -1
- data/lib/clacky/web/app.css +659 -75
- data/lib/clacky/web/app.js +0 -1
- data/lib/clacky/web/billing.js +371 -99
- data/lib/clacky/web/i18n.js +48 -2
- data/lib/clacky/web/index.html +34 -1
- data/lib/clacky/web/sessions.js +213 -82
- data/lib/clacky/web/settings.js +59 -17
- data/lib/clacky/web/workspace.js +204 -0
- data/lib/clacky/web/ws-dispatcher.js +19 -3
- data/lib/clacky.rb +9 -3
- metadata +4 -5
- data/lib/clacky/tools/list_tasks.rb +0 -54
- data/lib/clacky/tools/redo_task.rb +0 -41
- data/lib/clacky/tools/undo_task.rb +0 -35
data/lib/clacky/tools/browser.rb
CHANGED
|
@@ -35,7 +35,7 @@ module Clacky
|
|
|
35
35
|
Workflow: open → snapshot(interactive:true) → act(ref=...). New tab from `open` is auto-selected; only use `focus` to switch back to a previously-opened tab.
|
|
36
36
|
snapshot: returns hierarchical a11y tree truncated to ~8KB. Use query="text" to seek, or offset=N to page.
|
|
37
37
|
act kinds: click | dblclick | type | fill | press | hover | scroll | drag | select | wait | evaluate | click_at
|
|
38
|
-
evaluate: `js` is a
|
|
38
|
+
evaluate: `js` is a JS expression, e.g. "document.title". For multi-line / async logic use an IIFE: "(async () => { const r = await fetch(...); return r.status })()". Result is JSON-encoded.
|
|
39
39
|
screenshot: expensive — pass `ref` to capture one element instead of the whole page.
|
|
40
40
|
DESC
|
|
41
41
|
self.tool_category = "web"
|
|
@@ -58,7 +58,7 @@ module Clacky
|
|
|
58
58
|
amount: { type: "integer", description: "act scroll pixels (default 300)" },
|
|
59
59
|
ms: { type: "integer", description: "act wait ms" },
|
|
60
60
|
selector: { type: "string", description: "act wait: text or CSS selector" },
|
|
61
|
-
js: { type: "string", description: "act evaluate: JS
|
|
61
|
+
js: { type: "string", description: "act evaluate: JS expression (use IIFE for multi-line/async)" },
|
|
62
62
|
target_ref: { type: "string", description: "act drag destination ref" },
|
|
63
63
|
values: { type: "array", items: { type: "string" }, description: "act select options" },
|
|
64
64
|
x: { type: "number", description: "click_at x px" },
|
|
@@ -264,7 +264,7 @@ module Clacky
|
|
|
264
264
|
url = require_url(opts)
|
|
265
265
|
return url if url.is_a?(Hash)
|
|
266
266
|
invalidate_page_cache!
|
|
267
|
-
mcp_call("navigate_page", { type: "url", url: url })
|
|
267
|
+
mcp_call("navigate_page", with_page({ type: "url", url: url }))
|
|
268
268
|
wait_for_page_ready
|
|
269
269
|
{ action: "navigate", success: true, profile: "user", url: url, output: "Navigated to: #{url}" }
|
|
270
270
|
|
|
@@ -385,19 +385,10 @@ module Clacky
|
|
|
385
385
|
pid ? args.merge(pageId: pid) : args
|
|
386
386
|
end
|
|
387
387
|
|
|
388
|
-
# Wrap user-supplied JS as a Chrome-DevTools-MCP `function` argument.
|
|
389
|
-
# We treat `js` as a function body so users can write `const x = ...; return x;`
|
|
390
|
-
# naturally. For pure expressions ("document.title"), we auto-prepend `return`
|
|
391
|
-
# so the result still flows back. Detection is conservative — the presence of
|
|
392
|
-
# `return` or any top-level statement keyword skips the auto-return.
|
|
393
388
|
private def build_evaluate_function(js)
|
|
394
389
|
body = js.to_s.strip
|
|
395
390
|
return "() => {}" if body.empty?
|
|
396
|
-
|
|
397
|
-
looks_like_statement = body.match?(/(^|[\s;{])(return|const|let|var|if|for|while|throw|try|switch|function|class|do|await|async)\b/) ||
|
|
398
|
-
body.include?(";")
|
|
399
|
-
body = "return (#{body})" unless looks_like_statement
|
|
400
|
-
"() => { #{body} }"
|
|
391
|
+
"() => (#{body})"
|
|
401
392
|
end
|
|
402
393
|
|
|
403
394
|
SCREENSHOT_MAX_WIDTH = 800
|
|
@@ -340,12 +340,6 @@ module Clacky
|
|
|
340
340
|
project_root: cwd || Dir.pwd
|
|
341
341
|
)
|
|
342
342
|
|
|
343
|
-
# WSL interop fix: Windows .exe processes inherit the PTY's stdin fd
|
|
344
|
-
# and attempt to use it as a Windows Console, causing them to hang
|
|
345
|
-
# indefinitely. Redirect stdin from /dev/null for any .exe invocation
|
|
346
|
-
# that doesn't already have an explicit stdin redirect.
|
|
347
|
-
safe_command = redirect_exe_stdin(safe_command)
|
|
348
|
-
|
|
349
343
|
# PowerShell 5 on Chinese Windows emits CP936/GBK by default; force
|
|
350
344
|
# UTF-8 so our PTY (which decodes as UTF-8) doesn't see ??? bytes.
|
|
351
345
|
safe_command = force_powershell_utf8(safe_command)
|
|
@@ -988,10 +982,31 @@ module Clacky
|
|
|
988
982
|
# exit codes are also swallowed so the *user* command's $? is what
|
|
989
983
|
# lands in `__clacky_ec`.
|
|
990
984
|
hooks_line = with_hooks ? hooks_prefix_for(session) : ""
|
|
991
|
-
|
|
985
|
+
# WSL interop fix: Windows .exe processes inherit the PTY slave fd
|
|
986
|
+
# as their stdin and treat it like a Windows Console — they sit
|
|
987
|
+
# there waiting for input nobody will ever send, hanging the whole
|
|
988
|
+
# session. Wrapping the user command's group with `</dev/null` gives
|
|
989
|
+
# every process inside it (including .exe interop children) an
|
|
990
|
+
# immediate EOF on stdin, so they exit cleanly.
|
|
991
|
+
#
|
|
992
|
+
# We only do this on WSL when the command actually mentions `.exe`,
|
|
993
|
+
# so Linux interactive commands like `read -p` / `python` REPL on
|
|
994
|
+
# non-WSL hosts (and on WSL when not invoking Windows binaries)
|
|
995
|
+
# keep their PTY stdin and continue to behave as before.
|
|
996
|
+
stdin_redirect = exe_needs_stdin_isolation?(command) ? " </dev/null" : ""
|
|
997
|
+
line = %Q|#{hooks_line}{ #{command}\n}#{stdin_redirect}; __clacky_ec=$?; printf "\n__CLACKY_DONE_#{token}_%s__\n" "$__clacky_ec"\n|
|
|
992
998
|
session.mutex.synchronize { session.writer.write(line) }
|
|
993
999
|
end
|
|
994
1000
|
|
|
1001
|
+
# True when the command should run with stdin redirected from
|
|
1002
|
+
# /dev/null. Currently only triggers on WSL when the command string
|
|
1003
|
+
# mentions a Windows `.exe` binary — see write_user_command for the
|
|
1004
|
+
# full rationale.
|
|
1005
|
+
private def exe_needs_stdin_isolation?(command)
|
|
1006
|
+
return false unless Clacky::Utils::EnvironmentDetector.wsl?
|
|
1007
|
+
command.to_s =~ /\.exe\b/i ? true : false
|
|
1008
|
+
end
|
|
1009
|
+
|
|
995
1010
|
# Build the "run hooks" prefix line. Empty string for shells where
|
|
996
1011
|
# we don't know how to introspect hook registries.
|
|
997
1012
|
private def hooks_prefix_for(session)
|
|
@@ -1508,31 +1523,12 @@ module Clacky
|
|
|
1508
1523
|
lines.last(DISPLAY_TAIL_LINES).join("\n")
|
|
1509
1524
|
end
|
|
1510
1525
|
|
|
1511
|
-
# WSL interop fix: Windows .exe processes inherit the PTY stdin fd
|
|
1512
|
-
# and try to use it as a Windows Console, which hangs indefinitely.
|
|
1513
|
-
# Detect .exe invocations and redirect stdin from /dev/null unless
|
|
1514
|
-
# the command already has an explicit stdin redirect.
|
|
1515
|
-
private def redirect_exe_stdin(command)
|
|
1516
|
-
return command unless command =~ /\.exe\b/i
|
|
1517
|
-
return command if command =~ /<\s*[^\s|&;]/
|
|
1518
|
-
|
|
1519
|
-
# If the command has a shell-level pipe, insert </dev/null before
|
|
1520
|
-
# the first pipe so only the .exe segment gets its stdin redirected,
|
|
1521
|
-
# rather than starving a downstream pipe reader (e.g. `tr`, `grep`).
|
|
1522
|
-
if command =~ /\|/
|
|
1523
|
-
command.sub(/\s*\|/, ' </dev/null |')
|
|
1524
|
-
else
|
|
1525
|
-
"#{command} </dev/null"
|
|
1526
|
-
end
|
|
1527
|
-
end
|
|
1528
|
-
|
|
1529
1526
|
# PowerShell 5 on Chinese Windows defaults [Console]::OutputEncoding
|
|
1530
1527
|
# to CP936/GBK; our PTY decodes as UTF-8 so non-ASCII output becomes
|
|
1531
1528
|
# `???`. Inject UTF-8 setup into the user's PowerShell command so the
|
|
1532
1529
|
# shell emits UTF-8 bytes regardless of host locale.
|
|
1533
1530
|
POWERSHELL_PREAMBLE =
|
|
1534
|
-
"[Console]::OutputEncoding=[Text.Encoding]::UTF8;"
|
|
1535
|
-
"$OutputEncoding=[Text.Encoding]::UTF8;"
|
|
1531
|
+
"[Console]::OutputEncoding=[Text.Encoding]::UTF8;"
|
|
1536
1532
|
|
|
1537
1533
|
# Only rewrites simple `powershell[.exe]` / `pwsh[.exe]` invocations.
|
|
1538
1534
|
# Skips -File / -EncodedCommand / commands already handling encoding /
|
data/lib/clacky/ui_interface.rb
CHANGED
|
@@ -26,7 +26,7 @@ module Clacky
|
|
|
26
26
|
# === Status messages ===
|
|
27
27
|
def show_info(message, prefix_newline: true); end
|
|
28
28
|
def show_warning(message); end
|
|
29
|
-
def show_error(message); end
|
|
29
|
+
def show_error(message, code: nil, top_up_url: nil); end
|
|
30
30
|
def show_success(message); end
|
|
31
31
|
def log(message, level: :info); end
|
|
32
32
|
|
|
@@ -43,10 +43,12 @@ module Clacky
|
|
|
43
43
|
.mp3 .mp4 .avi .mov .mkv .wav .flac
|
|
44
44
|
.ttf .otf .woff .woff2
|
|
45
45
|
.db .sqlite .bin .dat
|
|
46
|
+
.wps .et .dps
|
|
46
47
|
].freeze
|
|
47
48
|
|
|
48
49
|
GLOB_ALLOWED_BINARY_EXTENSIONS = %w[
|
|
49
50
|
.pdf .doc .docx .ppt .pptx .xls .xlsx .odt .odp .ods
|
|
51
|
+
.wps .et .dps
|
|
50
52
|
].freeze
|
|
51
53
|
|
|
52
54
|
LLM_BINARY_EXTENSIONS = %w[.png .jpg .jpeg .gif .webp .pdf].freeze
|
|
@@ -64,6 +66,7 @@ module Clacky
|
|
|
64
66
|
".docx" => :document, ".doc" => :document,
|
|
65
67
|
".xlsx" => :spreadsheet, ".xls" => :spreadsheet,
|
|
66
68
|
".pptx" => :presentation, ".ppt" => :presentation,
|
|
69
|
+
".wps" => :document, ".et" => :spreadsheet, ".dps" => :presentation,
|
|
67
70
|
".pdf" => :pdf,
|
|
68
71
|
".zip" => :zip, ".gz" => :zip, ".tgz" => :zip, ".tar" => :zip, ".rar" => :zip, ".7z" => :zip,
|
|
69
72
|
".png" => :image, ".jpg" => :image, ".jpeg" => :image,
|
|
@@ -30,6 +30,9 @@ module Clacky
|
|
|
30
30
|
".xls" => "xlsx_parser.rb",
|
|
31
31
|
".pptx" => "pptx_parser.rb",
|
|
32
32
|
".ppt" => "pptx_parser.rb",
|
|
33
|
+
".wps" => "wps_parser.rb",
|
|
34
|
+
".et" => "wps_parser.rb",
|
|
35
|
+
".dps" => "wps_parser.rb",
|
|
33
36
|
}.freeze
|
|
34
37
|
|
|
35
38
|
# Ensure ~/.clacky/parsers/ exists and all default parsers are present.
|
data/lib/clacky/version.rb
CHANGED