openclacky 1.3.6 → 1.3.8
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 +54 -0
- data/lib/clacky/agent/hook_manager.rb +10 -1
- data/lib/clacky/agent/system_prompt_builder.rb +1 -1
- data/lib/clacky/agent.rb +17 -3
- data/lib/clacky/agent_profile.rb +124 -53
- data/lib/clacky/billing/billing_store.rb +3 -2
- data/lib/clacky/brand_config.rb +309 -6
- data/lib/clacky/cli.rb +4 -57
- data/lib/clacky/default_extensions/coding/agents/coding/avatar.png +0 -0
- data/lib/clacky/default_extensions/coding/ext.yml +19 -0
- data/lib/clacky/default_extensions/ext-studio/agents/ext-developer/avatar.png +0 -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 +33 -0
- data/lib/clacky/default_extensions/ext-studio/panels/studio/view.js +1288 -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/agents/general/avatar.png +0 -0
- data/lib/clacky/default_extensions/general/ext.yml +19 -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} +4 -5
- 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 -90
- 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 +489 -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 +439 -146
- data/lib/clacky/server/session_registry.rb +9 -2
- data/lib/clacky/shell_hook_loader.rb +266 -22
- data/lib/clacky/skill_loader.rb +28 -1
- data/lib/clacky/ui2/components/welcome_banner.rb +1 -1
- data/lib/clacky/ui2/ui_controller.rb +9 -6
- data/lib/clacky/utils/workspace_rules.rb +2 -2
- data/lib/clacky/version.rb +1 -1
- data/lib/clacky/web/app.css +1034 -675
- 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 +432 -89
- 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 +390 -0
- data/lib/clacky/web/features/new-session/store.js +199 -0
- data/lib/clacky/web/features/new-session/view.js +619 -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 +48 -12
- 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 +152 -106
- data/lib/clacky/web/index.html +172 -190
- data/lib/clacky/web/sessions.js +67 -272
- data/lib/clacky/web/settings.js +1 -1
- data/lib/clacky/web/skills.js +88 -48
- data/lib/clacky/web/theme.js +5 -0
- data/lib/clacky/web/ws-dispatcher.js +12 -2
- data/lib/clacky/web/ws.js +2 -0
- data/lib/clacky.rb +21 -10
- metadata +52 -21
- 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
|
@@ -0,0 +1,254 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
require "json"
|
|
4
|
+
require "tmpdir"
|
|
5
|
+
require "socket"
|
|
6
|
+
require "digest"
|
|
7
|
+
|
|
8
|
+
# Extension Studio — backend for the debug/publish panels. Mounted at
|
|
9
|
+
# /api/ext/ext-studio/. Reads the resolved extension tree, runs the verifier,
|
|
10
|
+
# packs and publishes local containers to the marketplace.
|
|
11
|
+
class ExtStudioExt < Clacky::ApiExtension
|
|
12
|
+
timeout 60
|
|
13
|
+
|
|
14
|
+
# GET /api/ext/ext-studio/extensions
|
|
15
|
+
# List every local-layer container with a units summary and verify status,
|
|
16
|
+
# so the debug panel can populate its picker and detail view.
|
|
17
|
+
get "/extensions" do
|
|
18
|
+
result = Clacky::ExtensionLoader.load_all(force: true)
|
|
19
|
+
issues = Clacky::ExtensionVerifier.verify(result)
|
|
20
|
+
|
|
21
|
+
exts = local_containers(result).map do |ext_id, container|
|
|
22
|
+
serialize_container(ext_id, container, result, issues)
|
|
23
|
+
end
|
|
24
|
+
exts.sort_by! { |e| -e[:mtime] }
|
|
25
|
+
|
|
26
|
+
json(extensions: exts)
|
|
27
|
+
end
|
|
28
|
+
|
|
29
|
+
# POST /api/ext/ext-studio/verify
|
|
30
|
+
# body: { ext_id? } — omit ext_id to verify the whole tree.
|
|
31
|
+
# Returns structured issues (same shape the CLI prints) plus resolved units.
|
|
32
|
+
post "/verify" do
|
|
33
|
+
ext_id = json_body["ext_id"].to_s.strip
|
|
34
|
+
result = Clacky::ExtensionLoader.load_all(force: true)
|
|
35
|
+
issues = Clacky::ExtensionVerifier.verify(result)
|
|
36
|
+
|
|
37
|
+
scoped = ext_id.empty? ? issues : issues.select { |i| i.ext == ext_id }
|
|
38
|
+
units = result.units
|
|
39
|
+
units = units.select { |u| u.ext_id == ext_id } unless ext_id.empty?
|
|
40
|
+
|
|
41
|
+
json(
|
|
42
|
+
ext_id: ext_id.empty? ? nil : ext_id,
|
|
43
|
+
units: units.map { |u| serialize_unit(u) },
|
|
44
|
+
issues: scoped.map { |i| serialize_issue(i) },
|
|
45
|
+
ok: scoped.none? { |i| i.level == :error }
|
|
46
|
+
)
|
|
47
|
+
end
|
|
48
|
+
|
|
49
|
+
# POST /api/ext/ext-studio/pack
|
|
50
|
+
# body: { ext_id }
|
|
51
|
+
# Packs a local container into a zip (into a temp dir) and reports its path.
|
|
52
|
+
post "/pack" do
|
|
53
|
+
ext_id = require_ext_id!
|
|
54
|
+
Dir.mktmpdir("clacky-ext-studio-pack") do |tmp|
|
|
55
|
+
res = Clacky::ExtensionPackager.pack(ext_id, out_dir: tmp)
|
|
56
|
+
json(ok: true, ext_id: res.ext_id, path: res.path)
|
|
57
|
+
end
|
|
58
|
+
rescue Clacky::ExtensionPackager::Error => e
|
|
59
|
+
error!(e.message, status: 422)
|
|
60
|
+
end
|
|
61
|
+
|
|
62
|
+
# POST /api/ext/ext-studio/publish
|
|
63
|
+
# body: { ext_id, force?, status?, changelog? }
|
|
64
|
+
# Packs then uploads to the marketplace. Requires the device to be bound to a
|
|
65
|
+
# platform account (device token). When unbound, returns a 428 with a hint so
|
|
66
|
+
# the UI can trigger the on-demand device-authorization flow.
|
|
67
|
+
post "/publish" do
|
|
68
|
+
ext_id = require_ext_id!
|
|
69
|
+
|
|
70
|
+
unless Clacky::Identity.load.bound?
|
|
71
|
+
error!("device not bound to a platform account; authorize this device to publish",
|
|
72
|
+
status: 428, needs_binding: true)
|
|
73
|
+
end
|
|
74
|
+
|
|
75
|
+
brand = Clacky::BrandConfig.load
|
|
76
|
+
|
|
77
|
+
Dir.mktmpdir("clacky-ext-studio-publish") do |tmp|
|
|
78
|
+
res = Clacky::ExtensionPackager.pack(ext_id, out_dir: tmp)
|
|
79
|
+
zip_data = File.binread(res.path)
|
|
80
|
+
|
|
81
|
+
result = brand.upload_extension!(
|
|
82
|
+
res.ext_id, zip_data,
|
|
83
|
+
force: json_body["force"] == true,
|
|
84
|
+
status: presence(json_body["status"]),
|
|
85
|
+
changelog: presence(json_body["changelog"])
|
|
86
|
+
)
|
|
87
|
+
|
|
88
|
+
if result[:success]
|
|
89
|
+
ext = result[:extension] || {}
|
|
90
|
+
ver = (ext["latest_version"] || {})["version"]
|
|
91
|
+
json(ok: true, ext_id: res.ext_id, version: ver, status: ext["status"])
|
|
92
|
+
elsif result[:already_exists]
|
|
93
|
+
json(ok: false, already_exists: true,
|
|
94
|
+
error: "#{res.ext_id} already published. Publish a new version with force.")
|
|
95
|
+
else
|
|
96
|
+
error!(result[:error] || "publish failed", status: 502)
|
|
97
|
+
end
|
|
98
|
+
end
|
|
99
|
+
rescue Clacky::ExtensionPackager::Error => e
|
|
100
|
+
error!(e.message, status: 422)
|
|
101
|
+
end
|
|
102
|
+
|
|
103
|
+
# GET /api/ext/ext-studio/published
|
|
104
|
+
# List the current user's published extensions from the marketplace.
|
|
105
|
+
get "/published" do
|
|
106
|
+
brand = Clacky::BrandConfig.load
|
|
107
|
+
result = brand.fetch_my_extensions!
|
|
108
|
+
error!(result[:error] || "failed to fetch published extensions", status: 502) unless result[:success]
|
|
109
|
+
|
|
110
|
+
exts = Array(result[:extensions]).map do |ext|
|
|
111
|
+
{
|
|
112
|
+
id: ext["name"] || ext["slug"] || ext["id"],
|
|
113
|
+
name: ext["name"],
|
|
114
|
+
version: (ext["latest_version"] || {})["version"] || ext["version"],
|
|
115
|
+
status: ext["status"],
|
|
116
|
+
units: ext["units"] || {}
|
|
117
|
+
}
|
|
118
|
+
end
|
|
119
|
+
json(extensions: exts)
|
|
120
|
+
end
|
|
121
|
+
|
|
122
|
+
# POST /api/ext/ext-studio/unpublish
|
|
123
|
+
# body: { ext_id }
|
|
124
|
+
post "/unpublish" do
|
|
125
|
+
ext_id = require_ext_id!
|
|
126
|
+
result = Clacky::BrandConfig.load.delete_extension!(ext_id)
|
|
127
|
+
error!(result[:error] || "unpublish failed", status: 502) unless result[:success]
|
|
128
|
+
json(ok: true, ext_id: ext_id)
|
|
129
|
+
end
|
|
130
|
+
|
|
131
|
+
# POST /api/ext/ext-studio/develop
|
|
132
|
+
# body: { idea? }
|
|
133
|
+
# Spawns a session bound to the ext-developer agent, optionally seeded with
|
|
134
|
+
# the user's idea as the first task — the "let AI build it for me" entry.
|
|
135
|
+
post "/develop" do
|
|
136
|
+
idea = presence(json_body["idea"])
|
|
137
|
+
name = idea ? "扩展开发: #{idea[0, 40]}" : "扩展开发"
|
|
138
|
+
sid = create_session(name: name, prompt: idea, profile: "ext-developer", source: :setup)
|
|
139
|
+
json(ok: true, session_id: sid)
|
|
140
|
+
end
|
|
141
|
+
|
|
142
|
+
# GET /api/ext/ext-studio/binding
|
|
143
|
+
# Reports whether this device is bound to a platform account, so the publish
|
|
144
|
+
# panel can decide up-front whether to run the binding flow.
|
|
145
|
+
get "/binding" do
|
|
146
|
+
identity = Clacky::Identity.load
|
|
147
|
+
json(bound: identity.bound?, user_id: identity.user_id)
|
|
148
|
+
end
|
|
149
|
+
|
|
150
|
+
# POST /api/ext/ext-studio/binding/start
|
|
151
|
+
# Kicks off an RFC 8628 device-authorization flow against the platform and
|
|
152
|
+
# returns the verification URL the panel opens plus the device_code to poll.
|
|
153
|
+
post "/binding/start" do
|
|
154
|
+
client = Clacky::PlatformHttpClient.new
|
|
155
|
+
result = client.post("/api/v1/device/authorize", {
|
|
156
|
+
device_id: binding_device_id,
|
|
157
|
+
device_info: { os: RUBY_PLATFORM, hostname: Socket.gethostname, app_version: Clacky::VERSION }
|
|
158
|
+
})
|
|
159
|
+
|
|
160
|
+
error!(result[:error] || "could not start authorization", status: 502) unless result[:success]
|
|
161
|
+
|
|
162
|
+
data = result[:data]
|
|
163
|
+
json(
|
|
164
|
+
ok: true,
|
|
165
|
+
device_code: data["device_code"],
|
|
166
|
+
user_code: data["user_code"],
|
|
167
|
+
verification_uri: data["verification_uri"],
|
|
168
|
+
verification_uri_complete: data["verification_uri_complete"],
|
|
169
|
+
interval: data["interval"] || 5
|
|
170
|
+
)
|
|
171
|
+
end
|
|
172
|
+
|
|
173
|
+
# POST /api/ext/ext-studio/binding/poll { device_code }
|
|
174
|
+
# Polls the platform once. On approval, binds the issued device token to the
|
|
175
|
+
# local Identity so subsequent publishes authenticate as the platform account.
|
|
176
|
+
post "/binding/poll" do
|
|
177
|
+
device_code = presence(json_body["device_code"])
|
|
178
|
+
error!("device_code required", status: 422) unless device_code
|
|
179
|
+
|
|
180
|
+
client = Clacky::PlatformHttpClient.new
|
|
181
|
+
result = client.post("/api/v1/device/token", { device_code: device_code })
|
|
182
|
+
data = result[:data] || {}
|
|
183
|
+
status = data["status"]
|
|
184
|
+
|
|
185
|
+
if result[:success] && status == "approved"
|
|
186
|
+
Clacky::Identity.load.bind!(
|
|
187
|
+
device_token: data["device_token"],
|
|
188
|
+
user_id: data["user_id"]
|
|
189
|
+
) if data["device_token"]
|
|
190
|
+
json(ok: true, status: "approved")
|
|
191
|
+
elsif status == "pending"
|
|
192
|
+
json(ok: true, status: "pending")
|
|
193
|
+
else
|
|
194
|
+
json(ok: false, status: status || "error", error: result[:error])
|
|
195
|
+
end
|
|
196
|
+
end
|
|
197
|
+
|
|
198
|
+
private def local_containers(result)
|
|
199
|
+
Array(result.containers).select { |_id, c| c[:layer] == :local }
|
|
200
|
+
end
|
|
201
|
+
|
|
202
|
+
private def serialize_container(ext_id, container, result, issues)
|
|
203
|
+
raw = container[:raw] || {}
|
|
204
|
+
ext_issues = issues.select { |i| i.ext == ext_id }
|
|
205
|
+
dir = container[:dir]
|
|
206
|
+
{
|
|
207
|
+
id: ext_id,
|
|
208
|
+
name: raw["name"] || ext_id,
|
|
209
|
+
description: raw["description"],
|
|
210
|
+
version: raw["version"],
|
|
211
|
+
origin: container[:origin],
|
|
212
|
+
layer: container[:layer].to_s,
|
|
213
|
+
dir: dir,
|
|
214
|
+
mtime: File.mtime(File.join(dir, "ext.yml")).to_i,
|
|
215
|
+
units: result.units.select { |u| u.ext_id == ext_id }.map { |u| serialize_unit(u) },
|
|
216
|
+
error_count: ext_issues.count { |i| i.level == :error },
|
|
217
|
+
warning_count: ext_issues.count { |i| i.level == :warning }
|
|
218
|
+
}
|
|
219
|
+
end
|
|
220
|
+
|
|
221
|
+
private def serialize_unit(unit)
|
|
222
|
+
{ kind: unit.kind.to_s, id: unit.id, layer: unit.layer.to_s }
|
|
223
|
+
end
|
|
224
|
+
|
|
225
|
+
private def serialize_issue(issue)
|
|
226
|
+
{
|
|
227
|
+
ext: issue.ext,
|
|
228
|
+
unit: issue.unit,
|
|
229
|
+
level: issue.level.to_s,
|
|
230
|
+
code: issue.code,
|
|
231
|
+
message: issue.message,
|
|
232
|
+
file: issue.file,
|
|
233
|
+
hint: issue.hint
|
|
234
|
+
}
|
|
235
|
+
end
|
|
236
|
+
|
|
237
|
+
private def require_ext_id!
|
|
238
|
+
id = json_body["ext_id"].to_s.strip
|
|
239
|
+
error!("ext_id required", status: 422) if id.empty?
|
|
240
|
+
id
|
|
241
|
+
end
|
|
242
|
+
|
|
243
|
+
private def presence(value)
|
|
244
|
+
str = value.to_s.strip
|
|
245
|
+
str.empty? ? nil : str
|
|
246
|
+
end
|
|
247
|
+
|
|
248
|
+
# Stable per-machine id for the device-authorization flow. Matches the
|
|
249
|
+
# onboarding device_id so binding reuses the same device row on the platform.
|
|
250
|
+
private def binding_device_id
|
|
251
|
+
components = [Socket.gethostname, ENV["USER"] || ENV["USERNAME"] || "", RUBY_PLATFORM]
|
|
252
|
+
Digest::SHA256.hexdigest(components.join(":"))
|
|
253
|
+
end
|
|
254
|
+
end
|
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
id: ext-studio
|
|
2
|
+
name: Extension Studio
|
|
3
|
+
description: AI-driven workspace for building, debugging, and publishing OpenClacky extensions
|
|
4
|
+
version: "0.1.0"
|
|
5
|
+
author: OpenClacky
|
|
6
|
+
homepage: https://www.openclacky.com
|
|
7
|
+
license: MIT
|
|
8
|
+
origin: self
|
|
9
|
+
contributes:
|
|
10
|
+
api: api/handler.rb
|
|
11
|
+
panels:
|
|
12
|
+
- id: studio
|
|
13
|
+
title: Extension & Creation
|
|
14
|
+
title_zh: 扩展与创作
|
|
15
|
+
description: Build, debug and publish extensions and skills
|
|
16
|
+
description_zh: 开发、调试、发布扩展与 skill
|
|
17
|
+
view: panels/studio/view.js
|
|
18
|
+
order: 210
|
|
19
|
+
agents:
|
|
20
|
+
- id: ext-developer
|
|
21
|
+
title: Extension Developer
|
|
22
|
+
title_zh: 扩展开发
|
|
23
|
+
description: AI expert that helps you build, debug, and publish OpenClacky extensions
|
|
24
|
+
description_zh: 帮你开发、调试、发布 OpenClacky 扩展的 AI 专家
|
|
25
|
+
order: 5
|
|
26
|
+
prompt: agents/ext-developer/system_prompt.md
|
|
27
|
+
avatar: agents/ext-developer/avatar.png
|
|
28
|
+
panels: [studio]
|
|
29
|
+
skills: [ext-scaffold, ext-debug, ext-publish]
|
|
30
|
+
skills:
|
|
31
|
+
- id: ext-scaffold
|
|
32
|
+
- id: ext-debug
|
|
33
|
+
- id: ext-publish
|