openclacky 1.3.6 → 1.3.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 +30 -0
- data/lib/clacky/agent.rb +8 -1
- data/lib/clacky/agent_profile.rb +121 -53
- data/lib/clacky/brand_config.rb +309 -6
- data/lib/clacky/cli.rb +4 -57
- data/lib/clacky/default_extensions/coding/ext.yml +18 -0
- data/lib/clacky/default_extensions/ext-studio/agents/ext-developer/system_prompt.md +95 -0
- data/lib/clacky/default_extensions/ext-studio/api/handler.rb +254 -0
- data/lib/clacky/default_extensions/ext-studio/ext.yml +32 -0
- data/lib/clacky/default_extensions/ext-studio/panels/studio/view.js +1269 -0
- data/lib/clacky/default_extensions/ext-studio/skills/ext-debug/SKILL.md +71 -0
- data/lib/clacky/default_extensions/ext-studio/skills/ext-publish/SKILL.md +74 -0
- data/lib/clacky/default_extensions/ext-studio/skills/ext-scaffold/SKILL.md +65 -0
- data/lib/clacky/default_extensions/general/ext.yml +18 -0
- data/lib/clacky/default_extensions/git/ext.yml +12 -0
- data/lib/clacky/{default_agents/_panels/git/panel.js → default_extensions/git/panels/git/view.js} +1 -2
- data/lib/clacky/default_extensions/meeting/{handler.rb → api/handler.rb} +78 -26
- data/lib/clacky/default_extensions/meeting/ext.yml +15 -0
- data/lib/clacky/default_extensions/meeting/{meeting.js → panels/meeting/view.js} +266 -89
- data/lib/clacky/default_extensions/meeting/skills/meeting-summarizer/SKILL.md +2 -1
- data/lib/clacky/default_extensions/time_machine/ext.yml +12 -0
- data/lib/clacky/{default_agents/_panels/time_machine/panel.js → default_extensions/time_machine/panels/time_machine/view.js} +58 -47
- data/lib/clacky/default_skills/channel-manager/discord_setup.rb +1 -1
- data/lib/clacky/default_skills/extend-openclacky/SKILL.md +83 -18
- data/lib/clacky/{api_extension.rb → extension/api_extension.rb} +27 -11
- data/lib/clacky/extension/api_loader.rb +136 -0
- data/lib/clacky/extension/cli_commands.rb +226 -0
- data/lib/clacky/{server/api_extension_dispatcher.rb → extension/dispatcher.rb} +22 -9
- data/lib/clacky/extension/hook_loader.rb +77 -0
- data/lib/clacky/extension/loader.rb +483 -0
- data/lib/clacky/extension/packager.rb +226 -0
- data/lib/clacky/{patch_loader.rb → extension/patch_loader.rb} +45 -0
- data/lib/clacky/extension/scaffold/template_renderer.rb +64 -0
- data/lib/clacky/extension/scaffold/templates/full/README.md.erb +48 -0
- data/lib/clacky/extension/scaffold/templates/full/agents/designer.md +5 -0
- data/lib/clacky/extension/scaffold/templates/full/api/handler.rb.erb +16 -0
- data/lib/clacky/extension/scaffold/templates/full/channels/noop.rb.erb +40 -0
- data/lib/clacky/extension/scaffold/templates/full/ext.yml.erb +41 -0
- data/lib/clacky/extension/scaffold/templates/full/hooks/audit.rb +9 -0
- data/lib/clacky/extension/scaffold/templates/full/panels/dashboard/view.js.erb +194 -0
- data/lib/clacky/extension/scaffold/templates/full/patches/audit.rb +15 -0
- data/lib/clacky/extension/scaffold/templates/full/skills/__slug__-skill/SKILL.md.erb +8 -0
- data/lib/clacky/extension/scaffold/templates/hello/api/handler.rb.erb +9 -0
- data/lib/clacky/extension/scaffold/templates/hello/ext.yml.erb +12 -0
- data/lib/clacky/extension/scaffold/templates/hello/panels/hello/view.js.erb +31 -0
- data/lib/clacky/extension/scaffold.rb +55 -0
- data/lib/clacky/extension/verifier.rb +196 -0
- data/lib/clacky/identity.rb +70 -0
- data/lib/clacky/locales/en.rb +19 -1
- data/lib/clacky/locales/zh.rb +19 -1
- data/lib/clacky/platform_http_client.rb +18 -15
- data/lib/clacky/server/channel/extension_adapter_loader.rb +32 -0
- data/lib/clacky/server/channel.rb +6 -0
- data/lib/clacky/server/http_server.rb +313 -144
- data/lib/clacky/server/session_registry.rb +9 -2
- data/lib/clacky/skill_loader.rb +28 -1
- data/lib/clacky/ui2/ui_controller.rb +9 -6
- data/lib/clacky/version.rb +1 -1
- data/lib/clacky/web/app.css +748 -573
- data/lib/clacky/web/app.js +63 -15
- data/lib/clacky/web/auth.js +2 -0
- data/lib/clacky/web/components/notify.js +2 -0
- data/lib/clacky/web/components/sidebar.js +1 -5
- data/lib/clacky/web/core/ext.js +316 -79
- data/lib/clacky/web/features/backup/store.js +1 -0
- data/lib/clacky/web/features/backup/view.js +0 -1
- data/lib/clacky/web/features/brand/view.js +13 -5
- data/lib/clacky/web/features/extensions/store.js +184 -0
- data/lib/clacky/web/features/extensions/view.js +389 -0
- data/lib/clacky/web/features/new-session/store.js +186 -0
- data/lib/clacky/web/features/new-session/view.js +319 -0
- data/lib/clacky/web/features/share/view.js +63 -14
- data/lib/clacky/web/features/skills/store.js +3 -1
- data/lib/clacky/web/features/skills/view.js +1 -1
- data/lib/clacky/web/features/tasks/view.js +12 -0
- data/lib/clacky/web/features/trash/store.js +24 -6
- data/lib/clacky/web/features/trash/view.js +46 -9
- data/lib/clacky/web/features/workspace/store.js +2 -0
- data/lib/clacky/web/features/workspace/view.js +0 -1
- data/lib/clacky/web/i18n.js +118 -106
- data/lib/clacky/web/index.html +138 -188
- data/lib/clacky/web/sessions.js +64 -269
- data/lib/clacky/web/settings.js +1 -1
- data/lib/clacky/web/skills.js +2 -0
- data/lib/clacky/web/theme.js +2 -0
- data/lib/clacky/web/ws-dispatcher.js +1 -0
- data/lib/clacky/web/ws.js +2 -0
- data/lib/clacky.rb +21 -10
- metadata +55 -24
- data/benchmark/runner.rb +0 -441
- data/lib/clacky/api_extension_loader.rb +0 -168
- data/lib/clacky/default_agents/SOUL.md +0 -3
- data/lib/clacky/default_agents/USER.md +0 -1
- data/lib/clacky/default_agents/coding/profile.yml +0 -5
- data/lib/clacky/default_agents/coding/webui/.gitkeep +0 -0
- data/lib/clacky/default_agents/general/profile.yml +0 -2
- data/lib/clacky/default_extensions/meeting/meta.yml +0 -3
- data/lib/clacky/web/features/creator/store.js +0 -81
- data/lib/clacky/web/features/creator/view.js +0 -380
- /data/lib/clacky/{default_agents → default_extensions/coding/agents}/coding/system_prompt.md +0 -0
- /data/lib/clacky/{default_agents → default_extensions/general/agents}/general/system_prompt.md +0 -0
- /data/lib/clacky/{default_agents/base_prompt.md → prompts/base.md} +0 -0
|
@@ -1,168 +0,0 @@
|
|
|
1
|
-
# frozen_string_literal: true
|
|
2
|
-
|
|
3
|
-
require "fileutils"
|
|
4
|
-
|
|
5
|
-
module Clacky
|
|
6
|
-
# Discovers and loads user-defined HTTP API extensions from
|
|
7
|
-
# ~/.clacky/api_ext/<name>/handler.rb. Each handler is expected to define a
|
|
8
|
-
# subclass of Clacky::ApiExtension; the subclass is auto-registered with the
|
|
9
|
-
# framework and its routes become available under /api/ext/<name>/.
|
|
10
|
-
#
|
|
11
|
-
# A broken extension (syntax error, missing base class, route conflict) is
|
|
12
|
-
# isolated: skipped with a logged warning, never aborts the load of others.
|
|
13
|
-
module ApiExtensionLoader
|
|
14
|
-
DEFAULT_DIR = File.expand_path("~/.clacky/api_ext")
|
|
15
|
-
BUILTIN_DIR = File.expand_path("../default_extensions", __FILE__)
|
|
16
|
-
DISABLED_DIR = "_disabled"
|
|
17
|
-
|
|
18
|
-
Result = Struct.new(:loaded, :skipped, keyword_init: true)
|
|
19
|
-
|
|
20
|
-
class << self
|
|
21
|
-
def load_all(dir: DEFAULT_DIR, builtin: true)
|
|
22
|
-
result = Result.new(loaded: [], skipped: [])
|
|
23
|
-
Clacky::ApiExtension.reset_registry!
|
|
24
|
-
|
|
25
|
-
# Load built-in (gem-shipped) extensions first (lowest priority)
|
|
26
|
-
if builtin && Dir.exist?(BUILTIN_DIR)
|
|
27
|
-
Dir.glob(File.join(BUILTIN_DIR, "*", "handler.rb")).sort.each do |handler_path|
|
|
28
|
-
ext_dir = File.dirname(handler_path)
|
|
29
|
-
ext_id = File.basename(ext_dir)
|
|
30
|
-
next if ext_id.start_with?("_")
|
|
31
|
-
load_one(ext_id, ext_dir, handler_path, result)
|
|
32
|
-
end
|
|
33
|
-
end
|
|
34
|
-
|
|
35
|
-
# Load user extensions (higher priority — same ext_id overwrites built-in)
|
|
36
|
-
if Dir.exist?(dir)
|
|
37
|
-
Dir.glob(File.join(dir, "*", "handler.rb")).sort.each do |handler_path|
|
|
38
|
-
ext_dir = File.dirname(handler_path)
|
|
39
|
-
ext_id = File.basename(ext_dir)
|
|
40
|
-
next if ext_id == DISABLED_DIR || ext_id.start_with?("_")
|
|
41
|
-
load_one(ext_id, ext_dir, handler_path, result)
|
|
42
|
-
end
|
|
43
|
-
end
|
|
44
|
-
|
|
45
|
-
@last_result = result
|
|
46
|
-
log_summary(result)
|
|
47
|
-
result
|
|
48
|
-
end
|
|
49
|
-
|
|
50
|
-
def last_result
|
|
51
|
-
@last_result || load_all
|
|
52
|
-
end
|
|
53
|
-
|
|
54
|
-
def load_one(ext_id, ext_dir, handler_path, result)
|
|
55
|
-
meta = read_meta(ext_dir)
|
|
56
|
-
before = Clacky::ApiExtension.pending_subclasses.size
|
|
57
|
-
|
|
58
|
-
require handler_path
|
|
59
|
-
|
|
60
|
-
new_subclasses = Clacky::ApiExtension.pending_subclasses[before..] || []
|
|
61
|
-
klass = new_subclasses.last
|
|
62
|
-
|
|
63
|
-
unless klass
|
|
64
|
-
result.skipped << [ext_id, "no Clacky::ApiExtension subclass defined in handler.rb"]
|
|
65
|
-
log_skip(ext_id, result.skipped.last[1])
|
|
66
|
-
return
|
|
67
|
-
end
|
|
68
|
-
|
|
69
|
-
klass.ext_id = ext_id
|
|
70
|
-
klass.ext_dir = ext_dir
|
|
71
|
-
klass.meta = meta
|
|
72
|
-
|
|
73
|
-
if klass.routes.empty?
|
|
74
|
-
result.skipped << [ext_id, "no routes declared (use get/post/... DSL)"]
|
|
75
|
-
log_skip(ext_id, result.skipped.last[1])
|
|
76
|
-
Clacky::ApiExtension.registry.delete(ext_id)
|
|
77
|
-
return
|
|
78
|
-
end
|
|
79
|
-
|
|
80
|
-
if (gap = validate_public_endpoints(klass, meta))
|
|
81
|
-
result.skipped << [ext_id, gap]
|
|
82
|
-
log_skip(ext_id, gap)
|
|
83
|
-
return
|
|
84
|
-
end
|
|
85
|
-
|
|
86
|
-
Clacky::ApiExtension.register(ext_id, klass)
|
|
87
|
-
result.loaded << ext_id
|
|
88
|
-
public_count = klass.public_paths.size
|
|
89
|
-
suffix = public_count > 0 ? " (#{public_count} public)" : ""
|
|
90
|
-
Clacky::Logger.info("[ApiExtensionLoader] Loaded '#{ext_id}' — #{klass.routes.size} route(s)#{suffix}")
|
|
91
|
-
rescue StandardError, ScriptError => e
|
|
92
|
-
result.skipped << [ext_id, e.message]
|
|
93
|
-
log_skip(ext_id, e.message)
|
|
94
|
-
end
|
|
95
|
-
|
|
96
|
-
private def read_meta(ext_dir)
|
|
97
|
-
path = File.join(ext_dir, "meta.yml")
|
|
98
|
-
return {} unless File.exist?(path)
|
|
99
|
-
|
|
100
|
-
YAMLCompat.load_file(path) || {}
|
|
101
|
-
rescue StandardError => e
|
|
102
|
-
Clacky::Logger.warn("[ApiExtensionLoader] Failed to read meta.yml in #{ext_dir}: #{e.message}")
|
|
103
|
-
{}
|
|
104
|
-
end
|
|
105
|
-
|
|
106
|
-
private def validate_public_endpoints(klass, meta)
|
|
107
|
-
return nil if klass.public_paths.empty?
|
|
108
|
-
return nil if meta["public"] == true
|
|
109
|
-
|
|
110
|
-
"uses public_endpoint but meta.yml is missing 'public: true'"
|
|
111
|
-
end
|
|
112
|
-
|
|
113
|
-
private def log_skip(ext_id, reason)
|
|
114
|
-
Clacky::Logger.warn("[ApiExtensionLoader] Skipped '#{ext_id}': #{reason}")
|
|
115
|
-
end
|
|
116
|
-
|
|
117
|
-
private def log_summary(result)
|
|
118
|
-
return if result.loaded.empty? && result.skipped.empty?
|
|
119
|
-
|
|
120
|
-
total_routes = result.loaded.sum { |id| Clacky::ApiExtension.registry[id]&.routes&.size || 0 }
|
|
121
|
-
Clacky::Logger.info("[ApiExtensionLoader] #{result.loaded.size} extension(s), #{total_routes} route(s); #{result.skipped.size} skipped")
|
|
122
|
-
end
|
|
123
|
-
|
|
124
|
-
# Generate a starter handler.rb at ~/.clacky/api_ext/<name>/handler.rb.
|
|
125
|
-
# Returns the path to the generated file.
|
|
126
|
-
def scaffold(name, dir: DEFAULT_DIR)
|
|
127
|
-
slug = name.to_s.strip.downcase.gsub(/[^a-z0-9_-]+/, "-").gsub(/\A-+|-+\z/, "")
|
|
128
|
-
raise ArgumentError, "invalid api_ext name: #{name.inspect}" if slug.empty?
|
|
129
|
-
|
|
130
|
-
target_dir = File.join(dir, slug)
|
|
131
|
-
path = File.join(target_dir, "handler.rb")
|
|
132
|
-
raise ArgumentError, "api_ext already exists: #{path}" if File.exist?(path)
|
|
133
|
-
|
|
134
|
-
FileUtils.mkdir_p(target_dir)
|
|
135
|
-
File.write(path, skeleton(slug))
|
|
136
|
-
path
|
|
137
|
-
end
|
|
138
|
-
|
|
139
|
-
private def skeleton(slug)
|
|
140
|
-
const = slug.split(/[-_]/).map(&:capitalize).join + "Ext"
|
|
141
|
-
<<~RUBY
|
|
142
|
-
# frozen_string_literal: true
|
|
143
|
-
|
|
144
|
-
# Custom HTTP API extension mounted at /api/ext/#{slug}/
|
|
145
|
-
# Scaffolded by `clacky api_ext_new #{slug}` — fill in the routes you need.
|
|
146
|
-
class #{const} < Clacky::ApiExtension
|
|
147
|
-
get "/hello" do
|
|
148
|
-
json(message: "hello from #{slug}")
|
|
149
|
-
end
|
|
150
|
-
|
|
151
|
-
# Examples — uncomment and adapt:
|
|
152
|
-
#
|
|
153
|
-
# post "/items" do
|
|
154
|
-
# body = json_body
|
|
155
|
-
# error!("name required", status: 422) unless body["name"]
|
|
156
|
-
# File.write(data_path("items.json"), body.to_json)
|
|
157
|
-
# json(ok: true)
|
|
158
|
-
# end
|
|
159
|
-
#
|
|
160
|
-
# get "/items/:id" do
|
|
161
|
-
# json(id: params[:id])
|
|
162
|
-
# end
|
|
163
|
-
end
|
|
164
|
-
RUBY
|
|
165
|
-
end
|
|
166
|
-
end
|
|
167
|
-
end
|
|
168
|
-
end
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
(No user profile configured yet. To personalize, create ~/.clacky/agents/USER.md)
|
|
File without changes
|
|
@@ -1,81 +0,0 @@
|
|
|
1
|
-
// ── Creator · store — cloud/local skill data + publish network ─────────────
|
|
2
|
-
//
|
|
3
|
-
// Owns the creator skill lists (cloud / local), loading flag, and the network
|
|
4
|
-
// calls (load catalog, publish a skill). It never renders.
|
|
5
|
-
//
|
|
6
|
-
// Emits store events the view reacts to; mirrors them to the extension bus via
|
|
7
|
-
// Clacky.ext.emit.
|
|
8
|
-
//
|
|
9
|
-
// `Creator` stays the single public facade.
|
|
10
|
-
//
|
|
11
|
-
// Depends on: Clacky.ext.
|
|
12
|
-
// ───────────────────────────────────────────────────────────────────────────
|
|
13
|
-
|
|
14
|
-
const CreatorStore = (() => {
|
|
15
|
-
let _cloudSkills = [];
|
|
16
|
-
let _localSkills = [];
|
|
17
|
-
let _loading = false;
|
|
18
|
-
|
|
19
|
-
const _listeners = {};
|
|
20
|
-
|
|
21
|
-
function _on(event, handler) {
|
|
22
|
-
(_listeners[event] ||= []).push(handler);
|
|
23
|
-
return () => {
|
|
24
|
-
const list = _listeners[event];
|
|
25
|
-
const i = list ? list.indexOf(handler) : -1;
|
|
26
|
-
if (i >= 0) list.splice(i, 1);
|
|
27
|
-
};
|
|
28
|
-
}
|
|
29
|
-
|
|
30
|
-
function _emit(event, payload) {
|
|
31
|
-
(_listeners[event] || []).forEach((h) => h(payload));
|
|
32
|
-
if (window.Clacky && Clacky.ext) Clacky.ext.emit(event, payload);
|
|
33
|
-
}
|
|
34
|
-
|
|
35
|
-
const state = {
|
|
36
|
-
get cloudSkills() { return _cloudSkills; },
|
|
37
|
-
get localSkills() { return _localSkills; },
|
|
38
|
-
get loading() { return _loading; },
|
|
39
|
-
};
|
|
40
|
-
|
|
41
|
-
const Creator = {
|
|
42
|
-
on: _on,
|
|
43
|
-
state,
|
|
44
|
-
|
|
45
|
-
async load() {
|
|
46
|
-
if (_loading) return;
|
|
47
|
-
_loading = true;
|
|
48
|
-
_emit("creator:loading");
|
|
49
|
-
try {
|
|
50
|
-
const res = await fetch("/api/creator/skills");
|
|
51
|
-
const data = await res.json();
|
|
52
|
-
if (!res.ok) throw new Error(data.error || "Load failed");
|
|
53
|
-
|
|
54
|
-
_cloudSkills = data.cloud_skills || [];
|
|
55
|
-
_localSkills = data.local_skills || [];
|
|
56
|
-
_emit("creator:changed", { platformFetchError: data.platform_fetch_error || null });
|
|
57
|
-
} catch (e) {
|
|
58
|
-
console.error("[Creator] load failed", e);
|
|
59
|
-
_emit("creator:error", { message: e.message });
|
|
60
|
-
} finally {
|
|
61
|
-
_loading = false;
|
|
62
|
-
}
|
|
63
|
-
},
|
|
64
|
-
|
|
65
|
-
/** Publish (or update) a skill. Returns { ok, already_exists, error }. */
|
|
66
|
-
async publish(skillName, { force = false } = {}) {
|
|
67
|
-
const url = `/api/my-skills/${encodeURIComponent(skillName)}/publish${force ? "?force=true" : ""}`;
|
|
68
|
-
const res = await fetch(url, { method: "POST" });
|
|
69
|
-
const data = await res.json();
|
|
70
|
-
return {
|
|
71
|
-
ok: res.ok && !!data.ok,
|
|
72
|
-
already_exists: !!data.already_exists,
|
|
73
|
-
error: data.error || null,
|
|
74
|
-
};
|
|
75
|
-
},
|
|
76
|
-
};
|
|
77
|
-
|
|
78
|
-
return Creator;
|
|
79
|
-
})();
|
|
80
|
-
|
|
81
|
-
const Creator = CreatorStore;
|
|
@@ -1,380 +0,0 @@
|
|
|
1
|
-
// ── Creator · view — Creator Hub rendering + publish UI + DOM wiring ───────
|
|
2
|
-
//
|
|
3
|
-
// Owns rendering of cloud/local skill cards, the publish progress UI (animation
|
|
4
|
-
// + overwrite confirm), the new-skill entry, and sidebar/panel visibility.
|
|
5
|
-
// Reads through CreatorStore.state; load/publish go through store actions.
|
|
6
|
-
//
|
|
7
|
-
// Augments the `Creator` facade with onPanelShow and updateSidebarVisibility.
|
|
8
|
-
//
|
|
9
|
-
// Depends on: CreatorStore, I18n, Brand, Skills, Modal.
|
|
10
|
-
// ───────────────────────────────────────────────────────────────────────────
|
|
11
|
-
|
|
12
|
-
const CreatorView = (() => {
|
|
13
|
-
let _domWired = false;
|
|
14
|
-
|
|
15
|
-
function escapeHtml(s) {
|
|
16
|
-
return String(s ?? "")
|
|
17
|
-
.replace(/&/g, "&").replace(/</g, "<")
|
|
18
|
-
.replace(/>/g, ">").replace(/"/g, """);
|
|
19
|
-
}
|
|
20
|
-
|
|
21
|
-
function _t(key) {
|
|
22
|
-
return I18n.t ? I18n.t(key) : key;
|
|
23
|
-
}
|
|
24
|
-
|
|
25
|
-
// ── Loading / error ─────────────────────────────────────────────────────
|
|
26
|
-
|
|
27
|
-
function _renderLoading() {
|
|
28
|
-
const cloudList = document.getElementById("creator-cloud-list");
|
|
29
|
-
const localList = document.getElementById("creator-local-list");
|
|
30
|
-
const placeholder = `<div class="creator-loading">${_t("creator.loading")}</div>`;
|
|
31
|
-
if (cloudList) cloudList.innerHTML = placeholder;
|
|
32
|
-
if (localList) localList.innerHTML = "";
|
|
33
|
-
}
|
|
34
|
-
|
|
35
|
-
function _renderError(payload) {
|
|
36
|
-
const cloudList = document.getElementById("creator-cloud-list");
|
|
37
|
-
if (cloudList) cloudList.innerHTML = `<div class="creator-empty creator-error">${escapeHtml(payload.message)}</div>`;
|
|
38
|
-
const localList = document.getElementById("creator-local-list");
|
|
39
|
-
if (localList) localList.innerHTML = "";
|
|
40
|
-
}
|
|
41
|
-
|
|
42
|
-
function _render(payload) {
|
|
43
|
-
_renderCloudSection();
|
|
44
|
-
_renderLocalSection();
|
|
45
|
-
_wireNewSkillEntry();
|
|
46
|
-
|
|
47
|
-
if (payload && payload.platformFetchError) {
|
|
48
|
-
_showNotice(
|
|
49
|
-
I18n.lang() === "zh"
|
|
50
|
-
? `平台数据加载失败:${payload.platformFetchError}`
|
|
51
|
-
: `Platform data unavailable: ${payload.platformFetchError}`,
|
|
52
|
-
"warn"
|
|
53
|
-
);
|
|
54
|
-
}
|
|
55
|
-
}
|
|
56
|
-
|
|
57
|
-
function _renderCloudSection() {
|
|
58
|
-
const list = document.getElementById("creator-cloud-list");
|
|
59
|
-
if (!list) return;
|
|
60
|
-
|
|
61
|
-
const cloudSkills = CreatorStore.state.cloudSkills;
|
|
62
|
-
if (cloudSkills.length === 0) {
|
|
63
|
-
list.innerHTML = `<div class="creator-empty">${_t("creator.cloud.empty")}</div>`;
|
|
64
|
-
return;
|
|
65
|
-
}
|
|
66
|
-
|
|
67
|
-
list.innerHTML = "";
|
|
68
|
-
cloudSkills.forEach(skill => list.appendChild(_buildCloudCard(skill)));
|
|
69
|
-
}
|
|
70
|
-
|
|
71
|
-
function _renderLocalSection() {
|
|
72
|
-
const list = document.getElementById("creator-local-list");
|
|
73
|
-
if (!list) return;
|
|
74
|
-
|
|
75
|
-
const localSkills = CreatorStore.state.localSkills;
|
|
76
|
-
if (localSkills.length === 0) {
|
|
77
|
-
list.innerHTML = `<div class="creator-empty">${_t("creator.local.empty")}</div>`;
|
|
78
|
-
return;
|
|
79
|
-
}
|
|
80
|
-
|
|
81
|
-
list.innerHTML = "";
|
|
82
|
-
localSkills.forEach(skill => list.appendChild(_buildLocalCard(skill)));
|
|
83
|
-
}
|
|
84
|
-
|
|
85
|
-
// ── Cloud card ───────────────────────────────────────────────────────────
|
|
86
|
-
|
|
87
|
-
function _buildCloudCard(skill) {
|
|
88
|
-
const card = document.createElement("div");
|
|
89
|
-
card.className = "creator-skill-card creator-cloud-card";
|
|
90
|
-
card.dataset.name = skill.name;
|
|
91
|
-
|
|
92
|
-
const versionHtml = skill.version
|
|
93
|
-
? `<span class="creator-version">v${escapeHtml(skill.version)}</span>`
|
|
94
|
-
: "";
|
|
95
|
-
|
|
96
|
-
const downloadHtml = typeof skill.download_count === "number"
|
|
97
|
-
? `<span class="creator-dl-count" title="${_t("creator.downloads")}">
|
|
98
|
-
<svg xmlns="http://www.w3.org/2000/svg" width="11" height="11" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2.5" stroke-linecap="round" stroke-linejoin="round">
|
|
99
|
-
<path d="M21 15v4a2 2 0 0 1-2 2H5a2 2 0 0 1-2-2v-4"/>
|
|
100
|
-
<polyline points="7 10 12 15 17 10"/>
|
|
101
|
-
<line x1="12" y1="15" x2="12" y2="3"/>
|
|
102
|
-
</svg>
|
|
103
|
-
${skill.download_count}
|
|
104
|
-
</span>`
|
|
105
|
-
: "";
|
|
106
|
-
|
|
107
|
-
const changesHtml = skill.has_local_changes
|
|
108
|
-
? `<span class="creator-changes-badge" title="${_t("creator.hasLocalChanges")}">● ${_t("creator.changed")}</span>`
|
|
109
|
-
: "";
|
|
110
|
-
|
|
111
|
-
let actionBtnsHtml = "";
|
|
112
|
-
if (skill.local_present) {
|
|
113
|
-
if (skill.has_local_changes) {
|
|
114
|
-
actionBtnsHtml = `
|
|
115
|
-
<div class="creator-upload-wrap">
|
|
116
|
-
<button class="btn-creator-publish" data-state="">
|
|
117
|
-
<svg class="btn-upload-icon" xmlns="http://www.w3.org/2000/svg" width="13" height="13" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2.5" stroke-linecap="round" stroke-linejoin="round">
|
|
118
|
-
<path d="M21 15v4a2 2 0 0 1-2 2H5a2 2 0 0 1-2-2v-4"/>
|
|
119
|
-
<polyline points="17 8 12 3 7 8"/>
|
|
120
|
-
<line x1="12" y1="3" x2="12" y2="15"/>
|
|
121
|
-
</svg>
|
|
122
|
-
<span class="btn-upload-label">${_t("creator.btn.update")}</span>
|
|
123
|
-
</button>
|
|
124
|
-
<div class="skill-upload-progress-wrap" style="display:none">
|
|
125
|
-
<div class="skill-upload-progress-bar"></div>
|
|
126
|
-
</div>
|
|
127
|
-
</div>
|
|
128
|
-
<button class="btn-creator-iterate" title="${_t("creator.btn.iterate")}">
|
|
129
|
-
<svg xmlns="http://www.w3.org/2000/svg" width="13" height="13" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round">
|
|
130
|
-
<polyline points="23 4 23 10 17 10"/>
|
|
131
|
-
<path d="M20.49 15a9 9 0 1 1-2.12-9.36L23 10"/>
|
|
132
|
-
</svg>
|
|
133
|
-
${_t("creator.btn.iterate")}
|
|
134
|
-
</button>`;
|
|
135
|
-
} else {
|
|
136
|
-
actionBtnsHtml = `
|
|
137
|
-
<button class="btn-creator-up-to-date" disabled title="${_t("creator.btn.upToDate")}">
|
|
138
|
-
<svg xmlns="http://www.w3.org/2000/svg" width="13" height="13" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2.5" stroke-linecap="round" stroke-linejoin="round">
|
|
139
|
-
<polyline points="20 6 9 17 4 12"/>
|
|
140
|
-
</svg>
|
|
141
|
-
${_t("creator.btn.upToDate")}
|
|
142
|
-
</button>
|
|
143
|
-
<button class="btn-creator-iterate" title="${_t("creator.btn.iterate")}">
|
|
144
|
-
<svg xmlns="http://www.w3.org/2000/svg" width="13" height="13" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round">
|
|
145
|
-
<polyline points="23 4 23 10 17 10"/>
|
|
146
|
-
<path d="M20.49 15a9 9 0 1 1-2.12-9.36L23 10"/>
|
|
147
|
-
</svg>
|
|
148
|
-
${_t("creator.btn.iterate")}
|
|
149
|
-
</button>`;
|
|
150
|
-
}
|
|
151
|
-
}
|
|
152
|
-
|
|
153
|
-
card.innerHTML = `
|
|
154
|
-
<div class="creator-card-main">
|
|
155
|
-
<div class="creator-card-info">
|
|
156
|
-
<div class="creator-card-title">
|
|
157
|
-
<span class="creator-skill-name">${escapeHtml(skill.name)}</span>
|
|
158
|
-
<span class="creator-badge creator-badge-published">${_t("creator.badge.published")}</span>
|
|
159
|
-
${changesHtml}
|
|
160
|
-
</div>
|
|
161
|
-
<div class="creator-card-desc">${escapeHtml(skill.description || "")}</div>
|
|
162
|
-
<div class="creator-card-meta">${versionHtml}${downloadHtml}</div>
|
|
163
|
-
</div>
|
|
164
|
-
<div class="creator-card-actions">${actionBtnsHtml}</div>
|
|
165
|
-
</div>`;
|
|
166
|
-
|
|
167
|
-
if (skill.local_present && skill.has_local_changes) {
|
|
168
|
-
const btn = card.querySelector(".btn-creator-publish");
|
|
169
|
-
const wrap = card.querySelector(".skill-upload-progress-wrap");
|
|
170
|
-
const bar = card.querySelector(".skill-upload-progress-bar");
|
|
171
|
-
btn.addEventListener("click", () => {
|
|
172
|
-
if (btn.disabled) return;
|
|
173
|
-
_publishSkill(skill.name, btn, wrap, bar, true, card, true);
|
|
174
|
-
});
|
|
175
|
-
}
|
|
176
|
-
|
|
177
|
-
if (skill.local_present) {
|
|
178
|
-
const iterBtn = card.querySelector(".btn-creator-iterate");
|
|
179
|
-
if (iterBtn) iterBtn.addEventListener("click", () => _iterateSkill(skill.name));
|
|
180
|
-
}
|
|
181
|
-
|
|
182
|
-
return card;
|
|
183
|
-
}
|
|
184
|
-
|
|
185
|
-
// ── Local card ─────────────────────────────────────────────────────────
|
|
186
|
-
|
|
187
|
-
function _buildLocalCard(skill) {
|
|
188
|
-
const card = document.createElement("div");
|
|
189
|
-
card.className = "creator-skill-card creator-local-card";
|
|
190
|
-
card.dataset.name = skill.name;
|
|
191
|
-
|
|
192
|
-
const shadowHtml = skill.shadowing_brand
|
|
193
|
-
? `<span class="creator-shadow-badge" title="${_t("creator.shadow.tooltip")}">⚡ ${_t("creator.shadow.label")}</span>`
|
|
194
|
-
: "";
|
|
195
|
-
|
|
196
|
-
card.innerHTML = `
|
|
197
|
-
<div class="creator-card-main">
|
|
198
|
-
<div class="creator-card-info">
|
|
199
|
-
<div class="creator-card-title">
|
|
200
|
-
<span class="creator-skill-name">${escapeHtml(skill.name)}</span>
|
|
201
|
-
<span class="creator-badge creator-badge-local">${_t("creator.badge.unpublished")}</span>
|
|
202
|
-
${shadowHtml}
|
|
203
|
-
</div>
|
|
204
|
-
<div class="creator-card-desc">${escapeHtml(skill.description || "")}</div>
|
|
205
|
-
</div>
|
|
206
|
-
<div class="creator-card-actions">
|
|
207
|
-
<div class="creator-upload-wrap">
|
|
208
|
-
<button class="btn-creator-publish" data-state="">
|
|
209
|
-
<svg class="btn-upload-icon" xmlns="http://www.w3.org/2000/svg" width="13" height="13" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2.5" stroke-linecap="round" stroke-linejoin="round">
|
|
210
|
-
<path d="M21 15v4a2 2 0 0 1-2 2H5a2 2 0 0 1-2-2v-4"/>
|
|
211
|
-
<polyline points="17 8 12 3 7 8"/>
|
|
212
|
-
<line x1="12" y1="3" x2="12" y2="15"/>
|
|
213
|
-
</svg>
|
|
214
|
-
<span class="btn-upload-label">${_t("creator.btn.publish")}</span>
|
|
215
|
-
</button>
|
|
216
|
-
<div class="skill-upload-progress-wrap" style="display:none">
|
|
217
|
-
<div class="skill-upload-progress-bar"></div>
|
|
218
|
-
</div>
|
|
219
|
-
</div>
|
|
220
|
-
</div>
|
|
221
|
-
</div>`;
|
|
222
|
-
|
|
223
|
-
const btn = card.querySelector(".btn-creator-publish");
|
|
224
|
-
const wrap = card.querySelector(".skill-upload-progress-wrap");
|
|
225
|
-
const bar = card.querySelector(".skill-upload-progress-bar");
|
|
226
|
-
btn.addEventListener("click", () => {
|
|
227
|
-
if (btn.disabled) return;
|
|
228
|
-
_publishSkill(skill.name, btn, wrap, bar, false, card, false);
|
|
229
|
-
});
|
|
230
|
-
|
|
231
|
-
return card;
|
|
232
|
-
}
|
|
233
|
-
|
|
234
|
-
// ── Publish UI ───────────────────────────────────────────────────────────
|
|
235
|
-
|
|
236
|
-
async function _publishSkill(skillName, publishBtn, progressWrap, progressBar, force, card, isUpdate) {
|
|
237
|
-
publishBtn.disabled = true;
|
|
238
|
-
publishBtn.dataset.state = "uploading";
|
|
239
|
-
const btnLabel = publishBtn.querySelector(".btn-upload-label");
|
|
240
|
-
if (btnLabel) btnLabel.textContent = I18n.lang() === "zh" ? "发布中…" : "Publishing…";
|
|
241
|
-
|
|
242
|
-
progressWrap.style.display = "block";
|
|
243
|
-
progressBar.style.width = "0%";
|
|
244
|
-
|
|
245
|
-
let animPct = 0;
|
|
246
|
-
const animInterval = setInterval(() => {
|
|
247
|
-
const remaining = 85 - animPct;
|
|
248
|
-
animPct += Math.max(1, remaining * 0.08);
|
|
249
|
-
if (animPct > 85) animPct = 85;
|
|
250
|
-
progressBar.style.width = animPct + "%";
|
|
251
|
-
}, 150);
|
|
252
|
-
|
|
253
|
-
let alreadyExists = false;
|
|
254
|
-
let skipFinalReset = false;
|
|
255
|
-
|
|
256
|
-
try {
|
|
257
|
-
const result = await Creator.publish(skillName, { force });
|
|
258
|
-
clearInterval(animInterval);
|
|
259
|
-
|
|
260
|
-
if (!result.ok) {
|
|
261
|
-
alreadyExists = result.already_exists;
|
|
262
|
-
throw new Error(result.error || "Publish failed");
|
|
263
|
-
}
|
|
264
|
-
|
|
265
|
-
progressBar.style.width = "100%";
|
|
266
|
-
progressBar.dataset.state = "success";
|
|
267
|
-
publishBtn.dataset.state = "success";
|
|
268
|
-
if (btnLabel) btnLabel.textContent = I18n.lang() === "zh" ? "已发布 ✓" : "Published ✓";
|
|
269
|
-
|
|
270
|
-
await new Promise(r => setTimeout(r, 1400));
|
|
271
|
-
await Creator.load();
|
|
272
|
-
skipFinalReset = true;
|
|
273
|
-
|
|
274
|
-
} catch (e) {
|
|
275
|
-
clearInterval(animInterval);
|
|
276
|
-
|
|
277
|
-
progressBar.style.width = "100%";
|
|
278
|
-
progressBar.dataset.state = "error";
|
|
279
|
-
publishBtn.dataset.state = "error";
|
|
280
|
-
if (btnLabel) btnLabel.textContent = I18n.lang() === "zh" ? "发布失败" : "Failed";
|
|
281
|
-
console.error("[Creator] publish failed", e);
|
|
282
|
-
publishBtn.title = e.message;
|
|
283
|
-
await new Promise(r => setTimeout(r, 2000));
|
|
284
|
-
|
|
285
|
-
if (alreadyExists && !force) {
|
|
286
|
-
skipFinalReset = true;
|
|
287
|
-
|
|
288
|
-
publishBtn.disabled = false;
|
|
289
|
-
publishBtn.dataset.state = "";
|
|
290
|
-
if (btnLabel) btnLabel.textContent = I18n.lang() === "zh" ? "更新" : "Update";
|
|
291
|
-
progressWrap.style.display = "none";
|
|
292
|
-
progressBar.style.width = "0%";
|
|
293
|
-
delete progressBar.dataset.state;
|
|
294
|
-
|
|
295
|
-
const confirmed = await Modal.confirm(
|
|
296
|
-
I18n.lang() === "zh"
|
|
297
|
-
? `"${skillName}" 已存在于平台。\n\n是否用当前版本覆盖?`
|
|
298
|
-
: `"${skillName}" already exists on the platform.\n\nOverwrite with the current version?`
|
|
299
|
-
);
|
|
300
|
-
if (confirmed) {
|
|
301
|
-
_publishSkill(skillName, publishBtn, progressWrap, progressBar, true, card, isUpdate);
|
|
302
|
-
}
|
|
303
|
-
}
|
|
304
|
-
} finally {
|
|
305
|
-
if (!skipFinalReset) {
|
|
306
|
-
publishBtn.disabled = false;
|
|
307
|
-
publishBtn.dataset.state = "";
|
|
308
|
-
if (btnLabel) {
|
|
309
|
-
btnLabel.textContent = isUpdate
|
|
310
|
-
? (I18n.lang() === "zh" ? "更新" : "Update")
|
|
311
|
-
: (I18n.lang() === "zh" ? "发布" : "Publish");
|
|
312
|
-
}
|
|
313
|
-
progressWrap.style.display = "none";
|
|
314
|
-
progressBar.style.width = "0%";
|
|
315
|
-
delete progressBar.dataset.state;
|
|
316
|
-
}
|
|
317
|
-
}
|
|
318
|
-
}
|
|
319
|
-
|
|
320
|
-
function _iterateSkill(skillName) {
|
|
321
|
-
Skills.createInSession(`/skill-creator ${I18n.t("creator.iterate.prompt")}${skillName}`);
|
|
322
|
-
}
|
|
323
|
-
|
|
324
|
-
function _wireNewSkillEntry() {
|
|
325
|
-
const entry = document.getElementById("creator-new-entry");
|
|
326
|
-
if (!entry || entry.dataset.wired) return;
|
|
327
|
-
entry.dataset.wired = "1";
|
|
328
|
-
entry.addEventListener("click", () => Skills.createInSession());
|
|
329
|
-
}
|
|
330
|
-
|
|
331
|
-
function _showNotice(msg, type = "warn") {
|
|
332
|
-
const container = document.getElementById("creator-cloud-list");
|
|
333
|
-
if (!container) return;
|
|
334
|
-
const old = container.querySelector(".creator-notice");
|
|
335
|
-
if (old) old.remove();
|
|
336
|
-
const el = document.createElement("div");
|
|
337
|
-
el.className = `creator-notice creator-notice-${type}`;
|
|
338
|
-
el.textContent = msg;
|
|
339
|
-
container.prepend(el);
|
|
340
|
-
}
|
|
341
|
-
|
|
342
|
-
function _subscribe() {
|
|
343
|
-
Creator.on("creator:loading", _renderLoading);
|
|
344
|
-
Creator.on("creator:changed", _render);
|
|
345
|
-
Creator.on("creator:error", _renderError);
|
|
346
|
-
}
|
|
347
|
-
|
|
348
|
-
const viewApi = {
|
|
349
|
-
/** Show/hide the creator section.
|
|
350
|
-
* Hidden for brand consumer users (branded=true, userLicensed=false).
|
|
351
|
-
* Visible for creators (userLicensed=true) and unbranded users. */
|
|
352
|
-
updateSidebarVisibility() {
|
|
353
|
-
const section = document.getElementById("creator-section");
|
|
354
|
-
if (!section) return;
|
|
355
|
-
const isBrandConsumer = Brand.branded && !Brand.userLicensed;
|
|
356
|
-
section.style.display = isBrandConsumer ? "none" : "";
|
|
357
|
-
},
|
|
358
|
-
|
|
359
|
-
/** Called by Router when the creator panel becomes active. */
|
|
360
|
-
onPanelShow() {
|
|
361
|
-
if (!_domWired) {
|
|
362
|
-
_domWired = true;
|
|
363
|
-
_wireNewSkillEntry();
|
|
364
|
-
}
|
|
365
|
-
const licensed = Brand.userLicensed;
|
|
366
|
-
const banner = document.getElementById("creator-promo-banner");
|
|
367
|
-
const lock = document.getElementById("creator-cloud-lock");
|
|
368
|
-
const list = document.getElementById("creator-cloud-list");
|
|
369
|
-
if (banner) banner.style.display = licensed ? "none" : "";
|
|
370
|
-
if (lock) lock.style.display = licensed ? "none" : "";
|
|
371
|
-
if (list) list.style.display = licensed ? "" : "none";
|
|
372
|
-
Creator.load();
|
|
373
|
-
},
|
|
374
|
-
};
|
|
375
|
-
|
|
376
|
-
return { init: _subscribe, api: viewApi };
|
|
377
|
-
})();
|
|
378
|
-
|
|
379
|
-
Object.assign(Creator, CreatorView.api);
|
|
380
|
-
CreatorView.init();
|
/data/lib/clacky/{default_agents → default_extensions/coding/agents}/coding/system_prompt.md
RENAMED
|
File without changes
|
/data/lib/clacky/{default_agents → default_extensions/general/agents}/general/system_prompt.md
RENAMED
|
File without changes
|
|
File without changes
|