openclacky 1.2.4 → 1.2.6
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 +32 -0
- data/lib/clacky/agent/cost_tracker.rb +17 -9
- data/lib/clacky/agent/llm_caller.rb +25 -3
- data/lib/clacky/agent/tool_executor.rb +14 -0
- data/lib/clacky/agent/tool_registry.rb +0 -7
- data/lib/clacky/agent.rb +0 -8
- data/lib/clacky/billing/billing_store.rb +62 -4
- data/lib/clacky/brand_config.rb +11 -0
- data/lib/clacky/cli.rb +74 -23
- data/lib/clacky/client.rb +36 -2
- data/lib/clacky/default_parsers/wps_parser.rb +82 -0
- data/lib/clacky/default_skills/browser-setup/SKILL.md +16 -90
- data/lib/clacky/message_format/anthropic.rb +13 -3
- data/lib/clacky/message_format/bedrock.rb +2 -2
- data/lib/clacky/platform_http_client.rb +28 -1
- data/lib/clacky/providers.rb +0 -27
- data/lib/clacky/server/http_server.rb +24 -4
- data/lib/clacky/tools/browser.rb +4 -13
- data/lib/clacky/tools/terminal.rb +18 -7
- data/lib/clacky/utils/file_processor.rb +3 -0
- data/lib/clacky/utils/parser_manager.rb +3 -0
- data/lib/clacky/utils/scripts_manager.rb +0 -1
- data/lib/clacky/version.rb +1 -1
- data/lib/clacky/web/app.css +422 -74
- data/lib/clacky/web/billing.js +371 -99
- data/lib/clacky/web/i18n.js +24 -2
- data/lib/clacky/web/index.html +1 -1
- data/lib/clacky/web/sessions.js +10 -68
- data/lib/clacky.rb +0 -3
- metadata +3 -6
- 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/scripts/wsl_network_doctor.ps1 +0 -196
data/lib/clacky/web/sessions.js
CHANGED
|
@@ -492,57 +492,12 @@ const Sessions = (() => {
|
|
|
492
492
|
const MAX_IMAGE_LONG_EDGE = 1920; // px — scale down if larger
|
|
493
493
|
const MAX_FILE_BYTES = 32 * 1024 * 1024; // 32 MB
|
|
494
494
|
const ACCEPTED_IMAGE_TYPES = ["image/png", "image/jpeg", "image/gif", "image/webp"];
|
|
495
|
-
const ACCEPTED_DOC_TYPES = [
|
|
496
|
-
"application/pdf",
|
|
497
|
-
"application/zip",
|
|
498
|
-
"application/x-zip-compressed",
|
|
499
|
-
"application/gzip",
|
|
500
|
-
"application/x-gzip",
|
|
501
|
-
"application/x-tar",
|
|
502
|
-
"application/x-compressed-tar",
|
|
503
|
-
"application/vnd.openxmlformats-officedocument.wordprocessingml.document", // .docx
|
|
504
|
-
"application/msword", // .doc
|
|
505
|
-
"application/vnd.openxmlformats-officedocument.spreadsheetml.sheet", // .xlsx
|
|
506
|
-
"application/vnd.ms-excel", // .xls
|
|
507
|
-
"application/vnd.openxmlformats-officedocument.presentationml.presentation", // .pptx
|
|
508
|
-
"application/vnd.ms-powerpoint", // .ppt
|
|
509
|
-
"text/csv", // .csv
|
|
510
|
-
"application/csv", // .csv (some browsers)
|
|
511
|
-
"text/markdown", // .md
|
|
512
|
-
"text/x-markdown", // .md (some browsers)
|
|
513
|
-
"text/plain", // .md / .txt (many browsers report this)
|
|
514
|
-
];
|
|
515
|
-
|
|
516
|
-
// Extension-based fallback for files whose MIME type is missing or unreliable.
|
|
517
|
-
// Browsers frequently report "" or "application/octet-stream" for .md / .tar.gz.
|
|
518
|
-
const ACCEPTED_DOC_EXTENSIONS = [
|
|
519
|
-
".pdf", ".zip",
|
|
520
|
-
".doc", ".docx", ".xls", ".xlsx", ".ppt", ".pptx",
|
|
521
|
-
".csv",
|
|
522
|
-
".md", ".markdown", ".txt", ".log",
|
|
523
|
-
".tar", ".gz", ".tgz", ".tar.gz", ".rar", ".7z"
|
|
524
|
-
];
|
|
525
|
-
|
|
526
|
-
function _hasAcceptedDocExt(filename) {
|
|
527
|
-
const lower = (filename || "").toLowerCase();
|
|
528
|
-
return ACCEPTED_DOC_EXTENSIONS.some(ext => lower.endsWith(ext));
|
|
529
|
-
}
|
|
530
|
-
|
|
531
|
-
function _isAcceptedDoc(file) {
|
|
532
|
-
if (!file) return false;
|
|
533
|
-
if (file.type && ACCEPTED_DOC_TYPES.includes(file.type)) return true;
|
|
534
|
-
return _hasAcceptedDocExt(file.name);
|
|
535
|
-
}
|
|
536
495
|
|
|
537
496
|
function _isAcceptedImage(file) {
|
|
538
497
|
if (!file) return false;
|
|
539
498
|
return ACCEPTED_IMAGE_TYPES.includes(file.type);
|
|
540
499
|
}
|
|
541
500
|
|
|
542
|
-
function _isAcceptedFile(file) {
|
|
543
|
-
return _isAcceptedImage(file) || _isAcceptedDoc(file);
|
|
544
|
-
}
|
|
545
|
-
|
|
546
501
|
function _docTypeIcon(mimeType, filename) {
|
|
547
502
|
const lower = (filename || "").toLowerCase();
|
|
548
503
|
if (mimeType === "application/pdf" || lower.endsWith(".pdf")) return "📄";
|
|
@@ -552,11 +507,11 @@ const Sessions = (() => {
|
|
|
552
507
|
lower.endsWith(".tar") || lower.endsWith(".gz") || lower.endsWith(".tgz") || lower.endsWith(".tar.gz") ||
|
|
553
508
|
lower.endsWith(".rar") || lower.endsWith(".7z")) return "🗜️";
|
|
554
509
|
if ((mimeType && mimeType.includes("wordprocessingml")) || mimeType === "application/msword" ||
|
|
555
|
-
lower.endsWith(".doc") || lower.endsWith(".docx")) return "📝";
|
|
510
|
+
lower.endsWith(".doc") || lower.endsWith(".docx") || lower.endsWith(".wps")) return "📝";
|
|
556
511
|
if ((mimeType && mimeType.includes("spreadsheetml")) || mimeType === "application/vnd.ms-excel" ||
|
|
557
|
-
lower.endsWith(".xls") || lower.endsWith(".xlsx")) return "📊";
|
|
512
|
+
lower.endsWith(".xls") || lower.endsWith(".xlsx") || lower.endsWith(".et")) return "📊";
|
|
558
513
|
if ((mimeType && mimeType.includes("presentationml")) || mimeType === "application/vnd.ms-powerpoint" ||
|
|
559
|
-
lower.endsWith(".ppt") || lower.endsWith(".pptx")) return "📋";
|
|
514
|
+
lower.endsWith(".ppt") || lower.endsWith(".pptx") || lower.endsWith(".dps")) return "📋";
|
|
560
515
|
if (mimeType === "text/csv" || mimeType === "application/csv" || lower.endsWith(".csv")) return "📊";
|
|
561
516
|
if (mimeType === "text/markdown" || mimeType === "text/x-markdown" ||
|
|
562
517
|
lower.endsWith(".md") || lower.endsWith(".markdown")) return "📝";
|
|
@@ -649,16 +604,10 @@ const Sessions = (() => {
|
|
|
649
604
|
}
|
|
650
605
|
|
|
651
606
|
function _addAttachmentFile(file) {
|
|
652
|
-
// Route by content category. Images must match known image MIME types
|
|
653
|
-
// (MIME is reliable for images). Documents fall back to extension-based
|
|
654
|
-
// detection because browsers frequently report "" or "application/octet-stream"
|
|
655
|
-
// for .md / .tar.gz files.
|
|
656
607
|
if (_isAcceptedImage(file)) {
|
|
657
608
|
_addImageFile(file);
|
|
658
|
-
} else if (_isAcceptedDoc(file)) {
|
|
659
|
-
_addGenericFile(file);
|
|
660
609
|
} else {
|
|
661
|
-
|
|
610
|
+
_addGenericFile(file);
|
|
662
611
|
}
|
|
663
612
|
}
|
|
664
613
|
|
|
@@ -848,27 +797,20 @@ const Sessions = (() => {
|
|
|
848
797
|
inputArea.addEventListener("drop", (e) => {
|
|
849
798
|
e.preventDefault();
|
|
850
799
|
inputArea.classList.remove("drag-over");
|
|
851
|
-
const files = Array.from(e.dataTransfer.files)
|
|
800
|
+
const files = Array.from(e.dataTransfer.files);
|
|
852
801
|
if (files.length === 0) return;
|
|
853
802
|
files.forEach(_addAttachmentFile);
|
|
854
803
|
});
|
|
855
804
|
|
|
856
|
-
// Paste handler — images and accepted docs from the clipboard.
|
|
857
805
|
document.getElementById("user-input").addEventListener("paste", (e) => {
|
|
858
806
|
const items = Array.from(e.clipboardData?.items || []);
|
|
859
|
-
|
|
860
|
-
// passes our doc filter. Must check name via getAsFile() for .md/.tar.gz
|
|
861
|
-
// (browsers often leave item.type empty for these).
|
|
862
|
-
const attachItems = items.filter(it => {
|
|
863
|
-
if (it.kind !== "file") return false;
|
|
864
|
-
if (ACCEPTED_IMAGE_TYPES.includes(it.type)) return true;
|
|
865
|
-
if (ACCEPTED_DOC_TYPES.includes(it.type)) return true;
|
|
866
|
-
const f = it.getAsFile && it.getAsFile();
|
|
867
|
-
return f ? _hasAcceptedDocExt(f.name) : false;
|
|
868
|
-
});
|
|
807
|
+
const attachItems = items.filter(it => it.kind === "file");
|
|
869
808
|
if (attachItems.length === 0) return;
|
|
870
809
|
e.preventDefault();
|
|
871
|
-
attachItems.forEach(it =>
|
|
810
|
+
attachItems.forEach(it => {
|
|
811
|
+
const f = it.getAsFile && it.getAsFile();
|
|
812
|
+
if (f) _addAttachmentFile(f);
|
|
813
|
+
});
|
|
872
814
|
});
|
|
873
815
|
}
|
|
874
816
|
|
data/lib/clacky.rb
CHANGED
|
@@ -118,9 +118,6 @@ require_relative "clacky/tools/todo_manager"
|
|
|
118
118
|
require_relative "clacky/tools/trash_manager"
|
|
119
119
|
require_relative "clacky/tools/request_user_feedback"
|
|
120
120
|
require_relative "clacky/tools/invoke_skill"
|
|
121
|
-
require_relative "clacky/tools/undo_task"
|
|
122
|
-
require_relative "clacky/tools/redo_task"
|
|
123
|
-
require_relative "clacky/tools/list_tasks"
|
|
124
121
|
require_relative "clacky/tools/browser"
|
|
125
122
|
require_relative "clacky/tools/terminal"
|
|
126
123
|
require_relative "clacky/mcp/client"
|
metadata
CHANGED
|
@@ -1,14 +1,14 @@
|
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
|
2
2
|
name: openclacky
|
|
3
3
|
version: !ruby/object:Gem::Version
|
|
4
|
-
version: 1.2.
|
|
4
|
+
version: 1.2.6
|
|
5
5
|
platform: ruby
|
|
6
6
|
authors:
|
|
7
7
|
- windy
|
|
8
8
|
autorequire:
|
|
9
9
|
bindir: bin
|
|
10
10
|
cert_chain: []
|
|
11
|
-
date: 2026-05-
|
|
11
|
+
date: 2026-05-28 00:00:00.000000000 Z
|
|
12
12
|
dependencies:
|
|
13
13
|
- !ruby/object:Gem::Dependency
|
|
14
14
|
name: faraday
|
|
@@ -360,6 +360,7 @@ files:
|
|
|
360
360
|
- lib/clacky/default_parsers/pdf_parser_ocr.py
|
|
361
361
|
- lib/clacky/default_parsers/pdf_parser_plumber.py
|
|
362
362
|
- lib/clacky/default_parsers/pptx_parser.rb
|
|
363
|
+
- lib/clacky/default_parsers/wps_parser.rb
|
|
363
364
|
- lib/clacky/default_parsers/xlsx_parser.rb
|
|
364
365
|
- lib/clacky/default_skills/browser-setup/SKILL.md
|
|
365
366
|
- lib/clacky/default_skills/channel-manager/SKILL.md
|
|
@@ -461,8 +462,6 @@ files:
|
|
|
461
462
|
- lib/clacky/tools/glob.rb
|
|
462
463
|
- lib/clacky/tools/grep.rb
|
|
463
464
|
- lib/clacky/tools/invoke_skill.rb
|
|
464
|
-
- lib/clacky/tools/list_tasks.rb
|
|
465
|
-
- lib/clacky/tools/redo_task.rb
|
|
466
465
|
- lib/clacky/tools/request_user_feedback.rb
|
|
467
466
|
- lib/clacky/tools/security.rb
|
|
468
467
|
- lib/clacky/tools/terminal.rb
|
|
@@ -472,7 +471,6 @@ files:
|
|
|
472
471
|
- lib/clacky/tools/terminal/session_manager.rb
|
|
473
472
|
- lib/clacky/tools/todo_manager.rb
|
|
474
473
|
- lib/clacky/tools/trash_manager.rb
|
|
475
|
-
- lib/clacky/tools/undo_task.rb
|
|
476
474
|
- lib/clacky/tools/web_fetch.rb
|
|
477
475
|
- lib/clacky/tools/web_search.rb
|
|
478
476
|
- lib/clacky/tools/write.rb
|
|
@@ -596,7 +594,6 @@ files:
|
|
|
596
594
|
- scripts/install_rails_deps.sh
|
|
597
595
|
- scripts/install_system_deps.sh
|
|
598
596
|
- scripts/uninstall.sh
|
|
599
|
-
- scripts/wsl_network_doctor.ps1
|
|
600
597
|
- sig/clacky.rbs
|
|
601
598
|
homepage: https://github.com/clacky-ai/openclacky
|
|
602
599
|
licenses:
|
|
@@ -1,54 +0,0 @@
|
|
|
1
|
-
# frozen_string_literal: true
|
|
2
|
-
|
|
3
|
-
module Clacky
|
|
4
|
-
module Tools
|
|
5
|
-
# Tool for listing task history (Time Machine feature)
|
|
6
|
-
class ListTasks < Base
|
|
7
|
-
self.tool_name = "list_tasks"
|
|
8
|
-
self.tool_description = "List recent tasks in the task history with summaries. " \
|
|
9
|
-
"Shows current task, past tasks, and future tasks (after undo). " \
|
|
10
|
-
"Use when user wants to see task history or choose which task to undo/redo to."
|
|
11
|
-
self.tool_category = "time_machine"
|
|
12
|
-
self.tool_parameters = {
|
|
13
|
-
type: "object",
|
|
14
|
-
properties: {
|
|
15
|
-
limit: {
|
|
16
|
-
type: "integer",
|
|
17
|
-
description: "Maximum number of recent tasks to show (default: 10)",
|
|
18
|
-
default: 10
|
|
19
|
-
}
|
|
20
|
-
}
|
|
21
|
-
}
|
|
22
|
-
|
|
23
|
-
def execute(agent:, limit: 10, **_args)
|
|
24
|
-
history = agent.get_task_history(limit: limit)
|
|
25
|
-
|
|
26
|
-
if history.empty?
|
|
27
|
-
return "No task history available."
|
|
28
|
-
end
|
|
29
|
-
|
|
30
|
-
lines = ["Task History:"]
|
|
31
|
-
history.each do |task|
|
|
32
|
-
indicator = case task[:status]
|
|
33
|
-
when :current then "→"
|
|
34
|
-
when :past then " "
|
|
35
|
-
when :future then "↯"
|
|
36
|
-
end
|
|
37
|
-
|
|
38
|
-
branch_indicator = task[:has_branches] ? " ⎇" : ""
|
|
39
|
-
lines << "#{indicator}#{branch_indicator} Task #{task[:task_id]}: #{task[:summary]}"
|
|
40
|
-
end
|
|
41
|
-
|
|
42
|
-
lines.join("\n")
|
|
43
|
-
end
|
|
44
|
-
|
|
45
|
-
def format_call(limit: 10, **_args)
|
|
46
|
-
"Listing task history (limit: #{limit})..."
|
|
47
|
-
end
|
|
48
|
-
|
|
49
|
-
def format_result(result)
|
|
50
|
-
result
|
|
51
|
-
end
|
|
52
|
-
end
|
|
53
|
-
end
|
|
54
|
-
end
|
|
@@ -1,41 +0,0 @@
|
|
|
1
|
-
# frozen_string_literal: true
|
|
2
|
-
|
|
3
|
-
module Clacky
|
|
4
|
-
module Tools
|
|
5
|
-
# Tool for redoing a task after undo (Time Machine feature)
|
|
6
|
-
class RedoTask < Base
|
|
7
|
-
self.tool_name = "redo_task"
|
|
8
|
-
self.tool_description = "Redo to a specific task after undo. Restores files to that task's state. " \
|
|
9
|
-
"Use when user wants to go forward to a future task or switch to a different branch."
|
|
10
|
-
self.tool_category = "time_machine"
|
|
11
|
-
self.tool_parameters = {
|
|
12
|
-
type: "object",
|
|
13
|
-
properties: {
|
|
14
|
-
task_id: {
|
|
15
|
-
type: "integer",
|
|
16
|
-
description: "The task ID to redo to (must be greater than current active task)"
|
|
17
|
-
}
|
|
18
|
-
},
|
|
19
|
-
required: ["task_id"]
|
|
20
|
-
}
|
|
21
|
-
|
|
22
|
-
def execute(agent:, task_id:, **_args)
|
|
23
|
-
result = agent.switch_to_task(task_id)
|
|
24
|
-
|
|
25
|
-
if result[:success]
|
|
26
|
-
result[:message]
|
|
27
|
-
else
|
|
28
|
-
"Error: #{result[:message]}"
|
|
29
|
-
end
|
|
30
|
-
end
|
|
31
|
-
|
|
32
|
-
def format_call(task_id:, **_args)
|
|
33
|
-
"Redoing to task #{task_id}..."
|
|
34
|
-
end
|
|
35
|
-
|
|
36
|
-
def format_result(result)
|
|
37
|
-
result
|
|
38
|
-
end
|
|
39
|
-
end
|
|
40
|
-
end
|
|
41
|
-
end
|
|
@@ -1,35 +0,0 @@
|
|
|
1
|
-
# frozen_string_literal: true
|
|
2
|
-
|
|
3
|
-
module Clacky
|
|
4
|
-
module Tools
|
|
5
|
-
# Tool for undoing the last task (Time Machine feature)
|
|
6
|
-
class UndoTask < Base
|
|
7
|
-
self.tool_name = "undo_task"
|
|
8
|
-
self.tool_description = "Undo the last task and restore files to previous state. " \
|
|
9
|
-
"Use when user wants to go back to previous state or undo recent changes."
|
|
10
|
-
self.tool_category = "time_machine"
|
|
11
|
-
self.tool_parameters = {
|
|
12
|
-
type: "object",
|
|
13
|
-
properties: {}
|
|
14
|
-
}
|
|
15
|
-
|
|
16
|
-
def execute(agent:, **_args)
|
|
17
|
-
result = agent.undo_last_task
|
|
18
|
-
|
|
19
|
-
if result[:success]
|
|
20
|
-
result[:message]
|
|
21
|
-
else
|
|
22
|
-
"Error: #{result[:message]}"
|
|
23
|
-
end
|
|
24
|
-
end
|
|
25
|
-
|
|
26
|
-
def format_call(**_args)
|
|
27
|
-
"Undoing last task..."
|
|
28
|
-
end
|
|
29
|
-
|
|
30
|
-
def format_result(result)
|
|
31
|
-
result
|
|
32
|
-
end
|
|
33
|
-
end
|
|
34
|
-
end
|
|
35
|
-
end
|
|
@@ -1,196 +0,0 @@
|
|
|
1
|
-
# wsl_network_doctor.ps1 — diagnose & repair WSL2 mirrored networking for the browser tool.
|
|
2
|
-
#
|
|
3
|
-
# Designed to be invoked from inside WSL via:
|
|
4
|
-
# powershell.exe -NoProfile -ExecutionPolicy Bypass -File <win-path-to-this-script> <subcommand>
|
|
5
|
-
#
|
|
6
|
-
# Subcommands:
|
|
7
|
-
# status Check whether mirrored networking is configured.
|
|
8
|
-
# enable Write networkingMode=mirrored to %USERPROFILE%\.wslconfig.
|
|
9
|
-
# repair Restart Windows Host Network Service (HNS) via UAC elevation.
|
|
10
|
-
#
|
|
11
|
-
# Exit codes (status only):
|
|
12
|
-
# 0 OK — mirrored configured, OR running on WSL1 (no config needed)
|
|
13
|
-
# 10 NEED_ENABLE — mirrored not configured, run `enable`
|
|
14
|
-
# 20 NEED_REPAIR — configured but suspected broken, run `repair`
|
|
15
|
-
# 1 unexpected error
|
|
16
|
-
#
|
|
17
|
-
# `enable` and `repair` exit 0 on success, 1 on failure.
|
|
18
|
-
|
|
19
|
-
param(
|
|
20
|
-
[Parameter(Position = 0)]
|
|
21
|
-
[ValidateSet('status', 'enable', 'repair')]
|
|
22
|
-
[string]$Command
|
|
23
|
-
)
|
|
24
|
-
|
|
25
|
-
$ErrorActionPreference = 'Stop'
|
|
26
|
-
|
|
27
|
-
# ---------------------------------------------------------------------------
|
|
28
|
-
# Helpers
|
|
29
|
-
# ---------------------------------------------------------------------------
|
|
30
|
-
|
|
31
|
-
function Get-WslConfigPath {
|
|
32
|
-
return (Join-Path $env:USERPROFILE '.wslconfig')
|
|
33
|
-
}
|
|
34
|
-
|
|
35
|
-
function Test-MirroredConfigured {
|
|
36
|
-
$cfg = Get-WslConfigPath
|
|
37
|
-
if (-not (Test-Path $cfg)) { return $false }
|
|
38
|
-
$content = Get-Content $cfg -Raw -ErrorAction SilentlyContinue
|
|
39
|
-
if ($null -eq $content) { return $false }
|
|
40
|
-
return ($content -match '(?im)^\s*networkingMode\s*=\s*mirrored\s*$')
|
|
41
|
-
}
|
|
42
|
-
|
|
43
|
-
# Returns 1 or 2 if Ubuntu is registered, $null otherwise.
|
|
44
|
-
# Parses `wsl.exe -l -v` output (UTF-16, may contain a star marker on default distro).
|
|
45
|
-
function Get-UbuntuWslVersion {
|
|
46
|
-
try {
|
|
47
|
-
$raw = & wsl.exe -l -v 2>$null
|
|
48
|
-
} catch {
|
|
49
|
-
return $null
|
|
50
|
-
}
|
|
51
|
-
if (-not $raw) { return $null }
|
|
52
|
-
|
|
53
|
-
foreach ($line in $raw) {
|
|
54
|
-
$clean = ($line -replace '\s+', ' ').Trim().TrimStart('*').Trim()
|
|
55
|
-
if ($clean -match '^Ubuntu(?:-[\w\.]+)?\s+\S+\s+(\d+)\s*$') {
|
|
56
|
-
return [int]$matches[1]
|
|
57
|
-
}
|
|
58
|
-
}
|
|
59
|
-
return $null
|
|
60
|
-
}
|
|
61
|
-
|
|
62
|
-
# ---------------------------------------------------------------------------
|
|
63
|
-
# Subcommand: status
|
|
64
|
-
# ---------------------------------------------------------------------------
|
|
65
|
-
|
|
66
|
-
function Invoke-Status {
|
|
67
|
-
$wslVer = Get-UbuntuWslVersion
|
|
68
|
-
if ($wslVer -eq 1) {
|
|
69
|
-
Write-Host "OK: Ubuntu is running on WSL1 — shares the Windows network stack directly."
|
|
70
|
-
Write-Host "No mirrored configuration needed. The browser tool can connect to 127.0.0.1 as-is."
|
|
71
|
-
exit 0
|
|
72
|
-
}
|
|
73
|
-
|
|
74
|
-
if (Test-MirroredConfigured) {
|
|
75
|
-
Write-Host "OK: mirrored networking is configured in .wslconfig."
|
|
76
|
-
Write-Host "If the browser tool still cannot connect, run: wsl_network_doctor.ps1 repair"
|
|
77
|
-
exit 0
|
|
78
|
-
}
|
|
79
|
-
|
|
80
|
-
Write-Host "NEED_ENABLE: mirrored networking is not configured."
|
|
81
|
-
Write-Host "Run: wsl_network_doctor.ps1 enable"
|
|
82
|
-
exit 10
|
|
83
|
-
}
|
|
84
|
-
|
|
85
|
-
# ---------------------------------------------------------------------------
|
|
86
|
-
# Subcommand: enable
|
|
87
|
-
# ---------------------------------------------------------------------------
|
|
88
|
-
|
|
89
|
-
function Invoke-Enable {
|
|
90
|
-
if (Test-MirroredConfigured) {
|
|
91
|
-
Write-Host "OK: already enabled. No changes needed."
|
|
92
|
-
Write-Host "If the browser tool still cannot connect, run: wsl_network_doctor.ps1 repair"
|
|
93
|
-
exit 0
|
|
94
|
-
}
|
|
95
|
-
|
|
96
|
-
$cfg = Get-WslConfigPath
|
|
97
|
-
Write-Host "Writing networkingMode=mirrored to $cfg ..."
|
|
98
|
-
|
|
99
|
-
if (-not (Test-Path $cfg)) {
|
|
100
|
-
New-Item -ItemType File -Path $cfg -Force | Out-Null
|
|
101
|
-
}
|
|
102
|
-
|
|
103
|
-
$content = Get-Content $cfg -Raw -ErrorAction SilentlyContinue
|
|
104
|
-
if ($null -eq $content) { $content = '' }
|
|
105
|
-
|
|
106
|
-
if ($content -match '(?im)^\s*networkingMode\s*=') {
|
|
107
|
-
$new = [regex]::Replace($content, '(?im)^\s*networkingMode\s*=.*$', 'networkingMode=mirrored')
|
|
108
|
-
Set-Content -Path $cfg -Value $new -NoNewline
|
|
109
|
-
} else {
|
|
110
|
-
if ($content -notmatch '(?im)^\[wsl2\]') {
|
|
111
|
-
if ($content.Length -gt 0 -and -not $content.EndsWith([char]10)) {
|
|
112
|
-
Add-Content -Path $cfg -Value ''
|
|
113
|
-
}
|
|
114
|
-
Add-Content -Path $cfg -Value '[wsl2]'
|
|
115
|
-
}
|
|
116
|
-
Add-Content -Path $cfg -Value 'networkingMode=mirrored'
|
|
117
|
-
}
|
|
118
|
-
|
|
119
|
-
Write-Host "WROTE: .wslconfig updated."
|
|
120
|
-
Write-Host ""
|
|
121
|
-
Write-Host "Next step (cannot be done from inside WSL):"
|
|
122
|
-
Write-Host " 1. Open Windows PowerShell"
|
|
123
|
-
Write-Host " 2. Run: wsl --shutdown"
|
|
124
|
-
Write-Host " 3. Reopen Clacky and run /browser-setup again"
|
|
125
|
-
exit 0
|
|
126
|
-
}
|
|
127
|
-
|
|
128
|
-
# ---------------------------------------------------------------------------
|
|
129
|
-
# Subcommand: repair
|
|
130
|
-
# ---------------------------------------------------------------------------
|
|
131
|
-
# Restart Windows Host Network Service (HNS). Requires admin → triggers UAC.
|
|
132
|
-
# Does NOT call `wsl --shutdown` here — the user must run it manually after
|
|
133
|
-
# the elevated window finishes, otherwise our own WSL session would be killed.
|
|
134
|
-
|
|
135
|
-
function Invoke-Repair {
|
|
136
|
-
Write-Host "Repairing Windows Host Network Service (HNS) ..."
|
|
137
|
-
Write-Host ""
|
|
138
|
-
Write-Host "A Windows User Account Control (UAC) dialog will appear."
|
|
139
|
-
Write-Host "Please click 'Yes' to allow the repair script to run."
|
|
140
|
-
Write-Host ""
|
|
141
|
-
|
|
142
|
-
$inner = @'
|
|
143
|
-
try {
|
|
144
|
-
Stop-Service hns -Force -ErrorAction SilentlyContinue
|
|
145
|
-
Start-Service hns -ErrorAction Stop
|
|
146
|
-
Write-Host "HNS restarted successfully."
|
|
147
|
-
} catch {
|
|
148
|
-
Write-Host "Repair failed: $_"
|
|
149
|
-
Start-Sleep 5
|
|
150
|
-
exit 1
|
|
151
|
-
}
|
|
152
|
-
Write-Host ""
|
|
153
|
-
Write-Host "Repair complete. Please run 'wsl --shutdown' in PowerShell, then reopen Clacky."
|
|
154
|
-
Start-Sleep 4
|
|
155
|
-
'@
|
|
156
|
-
|
|
157
|
-
$encoded = [Convert]::ToBase64String([Text.Encoding]::Unicode.GetBytes($inner))
|
|
158
|
-
|
|
159
|
-
try {
|
|
160
|
-
Start-Process powershell -Verb RunAs -ArgumentList '-NoProfile', '-EncodedCommand', $encoded
|
|
161
|
-
} catch {
|
|
162
|
-
Write-Host "FAILED: could not trigger UAC prompt: $_"
|
|
163
|
-
Write-Host ""
|
|
164
|
-
Write-Host "You can run the repair manually:"
|
|
165
|
-
Write-Host " 1. Open PowerShell as Administrator"
|
|
166
|
-
Write-Host " 2. Run: net stop hns; net start hns"
|
|
167
|
-
Write-Host " 3. Run: wsl --shutdown"
|
|
168
|
-
Write-Host " 4. Reopen Clacky"
|
|
169
|
-
exit 1
|
|
170
|
-
}
|
|
171
|
-
|
|
172
|
-
Write-Host "Repair script launched in an elevated PowerShell window."
|
|
173
|
-
Write-Host ""
|
|
174
|
-
Write-Host "After the elevated window finishes:"
|
|
175
|
-
Write-Host " 1. Run in regular PowerShell: wsl --shutdown"
|
|
176
|
-
Write-Host " 2. Reopen Clacky and run /browser-setup again"
|
|
177
|
-
exit 0
|
|
178
|
-
}
|
|
179
|
-
|
|
180
|
-
# ---------------------------------------------------------------------------
|
|
181
|
-
# Dispatch
|
|
182
|
-
# ---------------------------------------------------------------------------
|
|
183
|
-
|
|
184
|
-
switch ($Command) {
|
|
185
|
-
'status' { Invoke-Status }
|
|
186
|
-
'enable' { Invoke-Enable }
|
|
187
|
-
'repair' { Invoke-Repair }
|
|
188
|
-
default {
|
|
189
|
-
Write-Host "Usage: wsl_network_doctor.ps1 {status|enable|repair}"
|
|
190
|
-
Write-Host ""
|
|
191
|
-
Write-Host " status Check whether WSL2 mirrored networking is configured."
|
|
192
|
-
Write-Host " enable Write networkingMode=mirrored to %USERPROFILE%\.wslconfig."
|
|
193
|
-
Write-Host " repair Restart Windows Host Network Service (HNS) via UAC."
|
|
194
|
-
exit 2
|
|
195
|
-
}
|
|
196
|
-
}
|