dorkos 0.28.0 → 0.30.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.
package/README.md CHANGED
@@ -32,14 +32,14 @@ It's 7am. CI has been red since 2:47am. A dependency update cascaded across thre
32
32
 
33
33
  DorkOS gives your agents what they're missing: scheduling, communication, and coordination. The intelligence comes from the agents. Everything else comes from DorkOS.
34
34
 
35
- ### Pulse - Scheduling
35
+ ### Tasks - Scheduling
36
36
 
37
- Cron-based agent execution, independent of your IDE or terminal. Your agents ship code, triage issues, and run audits on schedule. You wake up to completed pull requests.
37
+ Cron-based and on-demand agent execution, independent of your IDE or terminal. Your agents ship code, triage issues, and run audits on schedule. You wake up to completed pull requests.
38
38
 
39
+ - File-based task definitions alongside your code
39
40
  - Overrun protection prevents duplicate runs
40
41
  - Isolated sessions per run with full history
41
42
  - Configurable concurrency limits
42
- - Approval gates for agent-created schedules
43
43
 
44
44
  ### Relay - Communication
45
45
 
@@ -70,6 +70,18 @@ Start a session from the browser. Check on it from your phone. Resume it from in
70
70
  - Real-time sync across multiple clients
71
71
  - Available in any browser or embedded in Obsidian
72
72
 
73
+ ### Extensions
74
+
75
+ Agents can build and install extensions that add new capabilities. Extensions define their own settings, secrets, and entry points — all managed through the UI.
76
+
77
+ ### MCP Server
78
+
79
+ DorkOS exposes an MCP server at `/mcp` (Streamable HTTP transport) so external AI agents — Claude Code, Cursor, Windsurf — can use DorkOS tools directly. Optional API key auth via `MCP_API_KEY`.
80
+
81
+ ```bash
82
+ claude mcp add dorkos --transport http http://localhost:4242/mcp
83
+ ```
84
+
73
85
  ## CLI Reference
74
86
 
75
87
  ```bash
@@ -77,6 +89,8 @@ dorkos # Start the server
77
89
  dorkos --port 8080 # Custom port
78
90
  dorkos --dir ~/projects # Custom working directory
79
91
  dorkos --tunnel # Enable remote access via ngrok
92
+ dorkos --tasks # Enable Tasks scheduler
93
+ dorkos --no-open # Don't open browser on startup
80
94
  dorkos config # Show all settings
81
95
  dorkos config set <key> <val> # Update a setting
82
96
  dorkos init # Interactive setup wizard
@@ -92,7 +106,12 @@ dorkos cleanup # Remove all DorkOS data
92
106
  | `-d, --dir <path>` | Working directory |
93
107
  | `-b, --boundary <path>` | Directory boundary (default: home directory) |
94
108
  | `-t, --tunnel` | Enable ngrok tunnel for remote access |
109
+ | `--tasks` / `--no-tasks` | Enable or disable the Tasks scheduler |
110
+ | `--no-open` | Don't open browser on startup |
95
111
  | `-l, --log-level <level>` | Log level (`fatal`, `error`, `warn`, `info`, `debug`, `trace`) |
112
+ | `--post-install-check` | Verify installation and exit |
113
+ | `-h, --help` | Show help message |
114
+ | `-v, --version` | Show version number |
96
115
 
97
116
  ### Config Subcommands
98
117
 
@@ -117,13 +136,19 @@ dorkos cleanup # Remove all DorkOS data
117
136
 
118
137
  ### Optional
119
138
 
120
- | Variable | Default | Description |
121
- | -------------------- | ----------------- | -------------------------------------- |
122
- | `DORKOS_PORT` | `4242` | Server port |
123
- | `DORKOS_HOST` | `localhost` | Server host (use `0.0.0.0` for Docker) |
124
- | `DORKOS_DEFAULT_CWD` | Current directory | Default working directory for sessions |
125
- | `DORKOS_BOUNDARY` | Home directory | Directory boundary root |
126
- | `LOG_LEVEL` | `info` | Log verbosity |
139
+ | Variable | Default | Description |
140
+ | ---------------------- | ----------------- | -------------------------------------- |
141
+ | `DORKOS_PORT` | `4242` | Server port |
142
+ | `DORKOS_HOST` | `localhost` | Server host (use `0.0.0.0` for Docker) |
143
+ | `DORKOS_DEFAULT_CWD` | Current directory | Default working directory for sessions |
144
+ | `DORKOS_BOUNDARY` | Home directory | Directory boundary root |
145
+ | `DORK_HOME` | `~/.dork` | Override data directory location |
146
+ | `LOG_LEVEL` | `info` | Log verbosity |
147
+ | `DORKOS_TASKS_ENABLED` | `true` | Enable or disable the Tasks scheduler |
148
+ | `DORKOS_OPEN` | `true` | Open browser on startup |
149
+ | `DORKOS_RELAY_ENABLED` | `true` | Enable the Relay message bus |
150
+ | `DORKOS_CORS_ORIGIN` | `localhost` | CORS allowed origin(s) |
151
+ | `MCP_API_KEY` | (none) | API key for MCP server authentication |
127
152
 
128
153
  ### Remote Access
129
154
 
package/dist/bin/cli.js CHANGED
@@ -14382,8 +14382,9 @@ var init_config_schema = __esm({
14382
14382
  server: external_exports.object({
14383
14383
  port: external_exports.number().int().min(1024).max(65535).default(4242),
14384
14384
  cwd: external_exports.string().nullable().default(null),
14385
- boundary: external_exports.string().nullable().default(null)
14386
- }).default(() => ({ port: 4242, cwd: null, boundary: null })),
14385
+ boundary: external_exports.string().nullable().default(null),
14386
+ open: external_exports.boolean().default(true)
14387
+ }).default(() => ({ port: 4242, cwd: null, boundary: null, open: true })),
14387
14388
  tunnel: external_exports.object({
14388
14389
  enabled: external_exports.boolean().default(false),
14389
14390
  domain: external_exports.string().nullable().default(null),
@@ -18780,7 +18781,7 @@ var require_main = __commonJS({
18780
18781
 
18781
18782
  // src/cli.ts
18782
18783
  import { parseArgs } from "node:util";
18783
- import os, { networkInterfaces } from "os";
18784
+ import os2, { networkInterfaces } from "os";
18784
18785
  import path4 from "path";
18785
18786
  import { fileURLToPath } from "url";
18786
18787
  import fs4 from "fs";
@@ -18877,7 +18878,165 @@ var cliEnvSchema = external_exports.object({
18877
18878
  });
18878
18879
  var env = cliEnvSchema.parse(process.env);
18879
18880
 
18881
+ // src/startup-diagnostics.ts
18882
+ import os from "os";
18883
+ var MIN_NODE_MAJOR = 20;
18884
+ function checkNodeVersion() {
18885
+ const major = parseInt(process.versions.node.split(".")[0], 10);
18886
+ if (major < MIN_NODE_MAJOR) {
18887
+ return {
18888
+ category: "node-version",
18889
+ headline: `Node.js ${process.versions.node} is not supported`,
18890
+ detail: `DorkOS requires Node.js ${MIN_NODE_MAJOR} or later. You are running ${process.versions.node}.`,
18891
+ fix: [
18892
+ `Upgrade Node.js to v${MIN_NODE_MAJOR}+ (LTS recommended):`,
18893
+ ` nvm install --lts # if using nvm`,
18894
+ ` brew install node # if using Homebrew`,
18895
+ ` https://nodejs.org # manual download`
18896
+ ].join("\n")
18897
+ };
18898
+ }
18899
+ return null;
18900
+ }
18901
+ function diagnoseStartupError(err) {
18902
+ const msg = err instanceof Error ? err.message : String(err);
18903
+ const code = err?.code;
18904
+ if (msg.includes("does not provide an export named") || msg.includes("is not a function") || msg.includes("Cannot find module") && msg.includes("claude-agent-sdk")) {
18905
+ const missingExport = msg.match(/export named '(\w+)'/)?.[1];
18906
+ return {
18907
+ category: "sdk-mismatch",
18908
+ headline: "Claude Agent SDK version mismatch",
18909
+ detail: missingExport ? `This version of DorkOS requires a newer Claude Agent SDK (missing export: ${missingExport}).` : `The installed Claude Agent SDK is incompatible with this version of DorkOS.`,
18910
+ fix: [
18911
+ "Update DorkOS and the SDK together:",
18912
+ " npm install -g dorkos@latest",
18913
+ "",
18914
+ "If you installed from source:",
18915
+ " pnpm install # updates all workspace dependencies"
18916
+ ].join("\n")
18917
+ };
18918
+ }
18919
+ if (code === "MODULE_NOT_FOUND" || msg.includes("Cannot find module")) {
18920
+ const modMatch = msg.match(/Cannot find module '([^']+)'/);
18921
+ const modName = modMatch?.[1] ?? "unknown";
18922
+ return {
18923
+ category: "module-not-found",
18924
+ headline: `Missing dependency: ${modName}`,
18925
+ detail: `A required module could not be found. This usually means a partial install or corrupted node_modules.`,
18926
+ fix: [
18927
+ "Reinstall DorkOS:",
18928
+ " npm install -g dorkos@latest",
18929
+ "",
18930
+ "Or clear the cache and retry:",
18931
+ " npm cache clean --force && npm install -g dorkos"
18932
+ ].join("\n")
18933
+ };
18934
+ }
18935
+ if (code === "EADDRINUSE") {
18936
+ const portMatch = msg.match(/(?:port\s+)?(\d{4,5})/i);
18937
+ const port2 = portMatch?.[1] ?? process.env.DORKOS_PORT ?? "4242";
18938
+ return {
18939
+ category: "port-conflict",
18940
+ headline: `Port ${port2} is already in use`,
18941
+ detail: `Another process is listening on port ${port2}. This could be another DorkOS instance or a different application.`,
18942
+ fix: [
18943
+ "Find what is using the port:",
18944
+ ` lsof -i :${port2}`,
18945
+ "",
18946
+ "Kill it if safe:",
18947
+ ` kill $(lsof -ti :${port2})`,
18948
+ "",
18949
+ "Or start DorkOS on a different port:",
18950
+ ` dorkos --port ${parseInt(port2) + 1}`
18951
+ ].join("\n")
18952
+ };
18953
+ }
18954
+ if (code === "EACCES" || code === "EPERM") {
18955
+ const pathMatch = msg.match(/'([^']+)'/);
18956
+ const filePath = pathMatch?.[1] ?? "~/.dork";
18957
+ return {
18958
+ category: "permission-denied",
18959
+ headline: "Permission denied",
18960
+ detail: `Cannot access ${filePath}. The data directory may be owned by a different user or have restricted permissions.`,
18961
+ fix: [
18962
+ "Fix ownership of the DorkOS data directory:",
18963
+ ` sudo chown -R $(whoami) ~/.dork`,
18964
+ "",
18965
+ "Or check if another user is running DorkOS:",
18966
+ ` ls -la ~/.dork`
18967
+ ].join("\n")
18968
+ };
18969
+ }
18970
+ if (msg.includes("SQLITE") || msg.includes("database") || msg.includes("migration") || msg.includes("drizzle")) {
18971
+ return {
18972
+ category: "db-error",
18973
+ headline: "Database error",
18974
+ detail: `The DorkOS database at ~/.dork/dork.db may be corrupted or incompatible: ${msg}`,
18975
+ fix: [
18976
+ "Back up and recreate the database:",
18977
+ " mv ~/.dork/dork.db ~/.dork/dork.db.bak",
18978
+ " dorkos # will create a fresh database",
18979
+ "",
18980
+ "Your agents and config are stored separately and will not be lost."
18981
+ ].join("\n")
18982
+ };
18983
+ }
18984
+ if (msg.includes("config") && (msg.includes("JSON") || msg.includes("parse") || msg.includes("schema"))) {
18985
+ return {
18986
+ category: "config-error",
18987
+ headline: "Invalid configuration",
18988
+ detail: `The config file at ~/.dork/config.json could not be parsed: ${msg}`,
18989
+ fix: [
18990
+ "Validate the config:",
18991
+ " dorkos config validate",
18992
+ "",
18993
+ "Or reset to defaults:",
18994
+ " dorkos config reset",
18995
+ "",
18996
+ "A backup of your previous config may be at ~/.dork/config.json.bak"
18997
+ ].join("\n")
18998
+ };
18999
+ }
19000
+ return {
19001
+ category: "unknown",
19002
+ headline: "Startup failed",
19003
+ detail: msg,
19004
+ fix: [
19005
+ "Check the logs for details:",
19006
+ ` cat ~/.dork/logs/dorkos-${(/* @__PURE__ */ new Date()).toISOString().slice(0, 10)}.ndjson | tail -20`,
19007
+ "",
19008
+ "If this persists, please report the issue:",
19009
+ " https://github.com/doriancollier/dorkos/issues"
19010
+ ].join("\n")
19011
+ };
19012
+ }
19013
+ function formatDiagnostic(diag) {
19014
+ const red = "\x1B[31m";
19015
+ const yellow = "\x1B[33m";
19016
+ const dim = "\x1B[2m";
19017
+ const bold = "\x1B[1m";
19018
+ const reset = "\x1B[0m";
19019
+ const lines = [
19020
+ "",
19021
+ `${red}${bold}\u2716 ${diag.headline}${reset}`,
19022
+ "",
19023
+ `${dim}${diag.detail}${reset}`,
19024
+ "",
19025
+ `${yellow}${bold}How to fix:${reset}`,
19026
+ diag.fix,
19027
+ "",
19028
+ `${dim}Platform: ${os.platform()} ${os.arch()} | Node: ${process.versions.node}${reset}`,
19029
+ ""
19030
+ ];
19031
+ return lines.join("\n");
19032
+ }
19033
+
18880
19034
  // src/cli.ts
19035
+ var nodeVersionIssue = checkNodeVersion();
19036
+ if (nodeVersionIssue) {
19037
+ console.error(formatDiagnostic(nodeVersionIssue));
19038
+ process.exit(1);
19039
+ }
18881
19040
  var __dirname = path4.dirname(fileURLToPath(import.meta.url));
18882
19041
  var values;
18883
19042
  var positionals;
@@ -18889,13 +19048,15 @@ try {
18889
19048
  dir: { type: "string", short: "d" },
18890
19049
  boundary: { type: "string", short: "b" },
18891
19050
  tasks: { type: "boolean" },
19051
+ open: { type: "boolean" },
18892
19052
  "log-level": { type: "string", short: "l" },
18893
19053
  help: { type: "boolean", short: "h" },
18894
19054
  version: { type: "boolean", short: "v" },
18895
19055
  "post-install-check": { type: "boolean", default: false },
18896
19056
  yes: { type: "boolean", short: "y", default: false }
18897
19057
  },
18898
- allowPositionals: true
19058
+ allowPositionals: true,
19059
+ allowNegative: true
18899
19060
  }));
18900
19061
  } catch (err) {
18901
19062
  if (err instanceof TypeError && err.code === "ERR_PARSE_ARGS_UNKNOWN_OPTION") {
@@ -18933,6 +19094,7 @@ Options:
18933
19094
  -b, --boundary <path> Directory boundary (default: home directory)
18934
19095
  --tasks Enable Tasks scheduler
18935
19096
  --no-tasks Disable Tasks scheduler
19097
+ --no-open Don't open browser on startup
18936
19098
  -l, --log-level <level> Log level (fatal|error|warn|info|debug|trace)
18937
19099
  --post-install-check Verify installation and exit
18938
19100
  -h, --help Show this help message
@@ -18955,16 +19117,16 @@ Examples:
18955
19117
  process.exit(0);
18956
19118
  }
18957
19119
  if (values.version) {
18958
- console.log("0.28.0");
19120
+ console.log("0.30.0");
18959
19121
  process.exit(0);
18960
19122
  }
18961
19123
  if (values["post-install-check"]) {
18962
19124
  checkClaude();
18963
- console.log(`dorkos ${"0.28.0"}`);
19125
+ console.log(`dorkos ${"0.30.0"}`);
18964
19126
  console.log("Installation verified.");
18965
19127
  process.exit(0);
18966
19128
  }
18967
- var DORK_HOME = env.DORK_HOME || path4.join(os.homedir(), ".dork");
19129
+ var DORK_HOME = env.DORK_HOME || path4.join(os2.homedir(), ".dork");
18968
19130
  var subcommand = positionals[0];
18969
19131
  if (subcommand === "cleanup") {
18970
19132
  const { runCleanup: runCleanup2 } = await Promise.resolve().then(() => (init_cleanup_command(), cleanup_command_exports));
@@ -19025,6 +19187,17 @@ if (values.tasks !== void 0) {
19025
19187
  } else if (!process.env.DORKOS_TASKS_ENABLED && cfgMgr.getDot("scheduler.enabled")) {
19026
19188
  process.env.DORKOS_TASKS_ENABLED = "true";
19027
19189
  }
19190
+ var shouldOpenBrowser = true;
19191
+ if (values.open !== void 0) {
19192
+ shouldOpenBrowser = Boolean(values.open);
19193
+ } else if (process.env.DORKOS_OPEN !== void 0) {
19194
+ shouldOpenBrowser = process.env.DORKOS_OPEN !== "false" && process.env.DORKOS_OPEN !== "0";
19195
+ } else {
19196
+ const configOpen = cfgMgr.getDot("server.open");
19197
+ if (configOpen !== void 0 && configOpen !== null) {
19198
+ shouldOpenBrowser = Boolean(configOpen);
19199
+ }
19200
+ }
19028
19201
  if (!process.env.DORKOS_RELAY_ENABLED && cfgMgr.getDot("relay.enabled")) {
19029
19202
  process.env.DORKOS_RELAY_ENABLED = "true";
19030
19203
  }
@@ -19045,7 +19218,7 @@ if (cliBoundary) {
19045
19218
  }
19046
19219
  }
19047
19220
  var boundaryVal = process.env.DORKOS_BOUNDARY;
19048
- var home = os.homedir();
19221
+ var home = os2.homedir();
19049
19222
  if (boundaryVal && !boundaryVal.startsWith(home + path4.sep) && boundaryVal !== home) {
19050
19223
  console.warn(
19051
19224
  `[Warning] Directory boundary "${boundaryVal}" is above home directory "${home}". This grants access to system directories.`
@@ -19066,11 +19239,17 @@ if (fs4.existsSync(envPath)) {
19066
19239
  const dotenv = await import("dotenv");
19067
19240
  dotenv.config({ path: envPath, override: false });
19068
19241
  }
19069
- await import("../server/index.js");
19242
+ try {
19243
+ await import("../server/index.js");
19244
+ } catch (err) {
19245
+ const diag = diagnoseStartupError(err);
19246
+ console.error(formatDiagnostic(diag));
19247
+ process.exit(1);
19248
+ }
19070
19249
  var port = process.env.DORKOS_PORT || String(DEFAULT_PORT);
19071
19250
  var localUrl = `http://localhost:${port}`;
19072
19251
  console.log("");
19073
- console.log(` DorkOS v${"0.28.0"}`);
19252
+ console.log(` DorkOS v${"0.30.0"}`);
19074
19253
  console.log(` Local: ${link(localUrl, localUrl)}`);
19075
19254
  var nets = networkInterfaces();
19076
19255
  var networkUrl = null;
@@ -19105,20 +19284,10 @@ if (process.env.TUNNEL_ENABLED) {
19105
19284
  }
19106
19285
  }
19107
19286
  console.log("");
19108
- if (process.stdin.isTTY) {
19109
- const { confirm: confirm3 } = await import("@inquirer/prompts");
19110
- try {
19111
- const shouldOpen = await confirm3({
19112
- message: "Open DorkOS in your browser?",
19113
- default: true
19114
- });
19115
- if (shouldOpen) {
19116
- const { exec } = await import("node:child_process");
19117
- const openCmd = process.platform === "darwin" ? "open" : process.platform === "win32" ? "start" : "xdg-open";
19118
- exec(`${openCmd} ${localUrl}`);
19119
- }
19120
- } catch {
19121
- }
19287
+ if (shouldOpenBrowser && process.stdin.isTTY) {
19288
+ const { exec } = await import("node:child_process");
19289
+ const openCmd = process.platform === "darwin" ? "open" : process.platform === "win32" ? "start" : "xdg-open";
19290
+ exec(`${openCmd} ${localUrl}`);
19122
19291
  }
19123
19292
  {
19124
19293
  const { tunnelManager: tunnelManager2 } = await Promise.resolve().then(() => (init_tunnel_manager(), tunnel_manager_exports));
@@ -19141,9 +19310,9 @@ if (process.stdin.isTTY) {
19141
19310
  }
19142
19311
  });
19143
19312
  }
19144
- checkForUpdate("0.28.0").then((latestVersion) => {
19313
+ checkForUpdate("0.30.0").then((latestVersion) => {
19145
19314
  if (latestVersion) {
19146
- const msg = `Update available: ${"0.28.0"} \u2192 ${latestVersion}`;
19315
+ const msg = `Update available: ${"0.30.0"} \u2192 ${latestVersion}`;
19147
19316
  const cmd = "Run npm install -g dorkos@latest to update";
19148
19317
  const width = Math.max(msg.length, cmd.length) + 6;
19149
19318
  const pad = (s2) => `\u2502 ${s2}${" ".repeat(width - s2.length - 6)} \u2502`;
@@ -1,4 +1,4 @@
1
- import{n as Odn,o as $Jn,p as DJn,r as mn,j as we,q as Ldn,t as _Jn,s as FJn,v as RJn,w as UX,A as $dn,x as Ddn,c as s5,Z as bpe,y as gpe,z as BJn,b as rA,D as JJn,F as GJn,X as HJn,G as qJn,H as T1n,m as zJn,I as UJn,J as KJn,K as XJn,L as WJn,N as VJn,O as QJn,Q as YJn,U as ZJn,V as eGn,W as nGn}from"./index-CdnIpAgi.js";/**
1
+ import{n as Odn,o as $Jn,p as DJn,r as mn,j as we,q as Ldn,t as _Jn,s as FJn,v as RJn,w as UX,A as $dn,x as Ddn,c as s5,Z as bpe,y as gpe,z as BJn,b as rA,D as JJn,F as GJn,X as HJn,G as qJn,H as T1n,m as zJn,I as UJn,J as KJn,K as XJn,L as WJn,N as VJn,O as QJn,Q as YJn,U as ZJn,V as eGn,W as nGn}from"./index-PgrCLMKB.js";/**
2
2
  * @license lucide-react v0.575.0 - ISC
3
3
  *
4
4
  * This source code is licensed under the ISC license.
@@ -1 +1 @@
1
- import{r,R as m,M as x,j as d,B as p}from"./index-CdnIpAgi.js";var B=({code:i,language:e,raw:a,className:g,startLine:u,...n})=>{let{shikiTheme:l}=r.useContext(m),t=x(),[h,s]=r.useState(a);return r.useEffect(()=>{if(!t){s(a);return}let o=t.highlight({code:i,language:e,themes:l},c=>{s(c)});o&&s(o)},[i,e,l,t,a]),d.jsx(p,{className:g,language:e,result:h,startLine:u,...n})};export{B as HighlightedCodeBlockBody};
1
+ import{r,R as m,M as x,j as d,B as p}from"./index-PgrCLMKB.js";var B=({code:i,language:e,raw:a,className:g,startLine:u,...n})=>{let{shikiTheme:l}=r.useContext(m),t=x(),[h,s]=r.useState(a);return r.useEffect(()=>{if(!t){s(a);return}let o=t.highlight({code:i,language:e,themes:l},c=>{s(c)});o&&s(o)},[i,e,l,t,a]),d.jsx(p,{className:g,language:e,result:h,startLine:u,...n})};export{B as HighlightedCodeBlockBody};
@@ -1 +1 @@
1
- import{r as b,j as e,T as f,C as v,P as N,a as E,c as g,b as k,S,u as w,d as y,e as C,f as T,g as R,h as z,t as m}from"./index-CdnIpAgi.js";import{E as M,i as _,k as H,l as B}from"./index-CdnIpAgi.js";const P=new Set(["disabled","discovered","incompatible","invalid"]);function I({extension:o,onToggle:u,isToggling:c}){const{manifest:s,status:t,scope:h,error:n}=o,[r,a]=b.useState(!1),x=!P.has(t),d=t==="incompatible",l=t==="invalid",i=t==="compile_error"||t==="activate_error",j=!d&&!l;return e.jsx("div",{"data-slot":"extension-card","data-testid":`extension-card-${o.id}`,className:g("bg-card rounded-xl border p-4",i&&"border-amber-500/50",(d||l)&&"opacity-70"),children:e.jsxs("div",{className:"flex items-start justify-between gap-4",children:[e.jsxs("div",{className:"min-w-0 flex-1 space-y-1.5",children:[e.jsxs("div",{className:"flex flex-wrap items-center gap-2",children:[i&&e.jsx(f,{className:"size-4 shrink-0 text-amber-500","aria-hidden":"true"}),(d||l)&&!i&&e.jsx(v,{className:"text-muted-foreground size-4 shrink-0","aria-hidden":"true"}),!i&&!d&&!l&&e.jsx(N,{className:"text-muted-foreground size-4 shrink-0","aria-hidden":"true"}),e.jsx("span",{className:"font-medium",children:s.name}),e.jsxs("span",{className:"text-muted-foreground text-sm",children:["v",s.version]})]}),s.description&&e.jsx("p",{className:"text-muted-foreground text-sm",children:s.description}),d&&s.minHostVersion&&e.jsxs("p",{className:"text-muted-foreground text-sm",children:["Requires DorkOS >= ",s.minHostVersion]}),i&&n&&e.jsxs("div",{className:"space-y-1",children:[e.jsxs("p",{className:"text-sm text-amber-600 dark:text-amber-400",children:[t==="compile_error"?"Compilation error: ":"Activation failed: ",n.message]}),n.details&&e.jsxs("button",{type:"button",onClick:()=>a(p=>!p),className:"text-muted-foreground hover:text-foreground flex items-center gap-1 text-xs transition-colors","aria-expanded":r,children:[e.jsx(E,{className:g("size-3 transition-transform",r&&"rotate-180")}),r?"Hide details":"Show details"]}),r&&n.details&&e.jsx("pre",{className:"bg-muted mt-1 max-h-32 overflow-auto rounded-md p-2 text-xs",children:n.details})]}),e.jsxs("div",{className:"text-muted-foreground flex flex-wrap items-center gap-2 text-xs",children:[e.jsx(k,{variant:"outline",className:"text-xs",children:h}),s.author&&e.jsx("span",{children:s.author})]})]}),e.jsx("div",{className:"pt-0.5",children:e.jsx(S,{checked:x,onCheckedChange:p=>u(o.id,p),disabled:!j||c,"aria-label":`${x?"Disable":"Enable"} ${s.name}`})})]})})}function A(){const{data:o=[],isLoading:u}=w(),c=y(),s=C(),t=T(),h=new Set([...c.variables?[c.variables]:[],...s.variables?[s.variables]:[]]);async function n(a,x){(x?c:s).mutate(a,{onSuccess:()=>{m.info("Extension changed — reload the page to apply",{action:{label:"Reload now",onClick:()=>location.reload()}})},onError:l=>{const i=x?"enable":"disable";m.error(`Failed to ${i} extension: ${l.message}`)}})}function r(){t.mutate(void 0,{onSuccess:a=>{m.success(`Reloaded ${a.length} extension(s)`)},onError:()=>{m.error("Failed to reload extensions")}})}return u?e.jsx("div",{className:"text-muted-foreground py-8 text-center text-sm",children:"Loading extensions…"}):e.jsxs("div",{className:"space-y-4",children:[e.jsxs("p",{className:"text-muted-foreground text-sm",children:["Extensions add new UI and capabilities to DorkOS. Place them in"," ",e.jsx("code",{className:"bg-muted rounded px-1",children:"~/.dork/extensions/"})," or"," ",e.jsx("code",{className:"bg-muted rounded px-1",children:".dork/extensions/"})," in your project."]}),o.length===0?e.jsx("div",{className:"text-muted-foreground py-8 text-center text-sm","data-testid":"no-extensions",children:e.jsx("p",{children:"No extensions installed."})}):e.jsx("div",{className:"space-y-3",children:o.map(a=>e.jsx(I,{extension:a,onToggle:n,isToggling:h.has(a.id)},a.id))}),e.jsx("div",{className:"flex justify-end",children:e.jsxs(R,{variant:"outline",size:"sm",onClick:r,disabled:t.isPending,"data-testid":"reload-extensions-button",children:[e.jsx(z,{className:g("mr-2 size-4",t.isPending&&"animate-spin")}),"Reload Extensions"]})})]})}export{M as ExtensionLoader,_ as ExtensionProvider,A as ExtensionsSettingsTab,H as createExtensionAPI,B as useExtensions};
1
+ import{r as b,j as e,T as f,C as v,P as N,a as E,c as g,b as k,S,u as w,d as y,e as C,f as T,g as R,h as z,t as m}from"./index-PgrCLMKB.js";import{E as M,i as _,k as H,l as B}from"./index-PgrCLMKB.js";const P=new Set(["disabled","discovered","incompatible","invalid"]);function I({extension:o,onToggle:u,isToggling:c}){const{manifest:s,status:t,scope:h,error:n}=o,[r,a]=b.useState(!1),x=!P.has(t),d=t==="incompatible",l=t==="invalid",i=t==="compile_error"||t==="activate_error",j=!d&&!l;return e.jsx("div",{"data-slot":"extension-card","data-testid":`extension-card-${o.id}`,className:g("bg-card rounded-xl border p-4",i&&"border-amber-500/50",(d||l)&&"opacity-70"),children:e.jsxs("div",{className:"flex items-start justify-between gap-4",children:[e.jsxs("div",{className:"min-w-0 flex-1 space-y-1.5",children:[e.jsxs("div",{className:"flex flex-wrap items-center gap-2",children:[i&&e.jsx(f,{className:"size-4 shrink-0 text-amber-500","aria-hidden":"true"}),(d||l)&&!i&&e.jsx(v,{className:"text-muted-foreground size-4 shrink-0","aria-hidden":"true"}),!i&&!d&&!l&&e.jsx(N,{className:"text-muted-foreground size-4 shrink-0","aria-hidden":"true"}),e.jsx("span",{className:"font-medium",children:s.name}),e.jsxs("span",{className:"text-muted-foreground text-sm",children:["v",s.version]})]}),s.description&&e.jsx("p",{className:"text-muted-foreground text-sm",children:s.description}),d&&s.minHostVersion&&e.jsxs("p",{className:"text-muted-foreground text-sm",children:["Requires DorkOS >= ",s.minHostVersion]}),i&&n&&e.jsxs("div",{className:"space-y-1",children:[e.jsxs("p",{className:"text-sm text-amber-600 dark:text-amber-400",children:[t==="compile_error"?"Compilation error: ":"Activation failed: ",n.message]}),n.details&&e.jsxs("button",{type:"button",onClick:()=>a(p=>!p),className:"text-muted-foreground hover:text-foreground flex items-center gap-1 text-xs transition-colors","aria-expanded":r,children:[e.jsx(E,{className:g("size-3 transition-transform",r&&"rotate-180")}),r?"Hide details":"Show details"]}),r&&n.details&&e.jsx("pre",{className:"bg-muted mt-1 max-h-32 overflow-auto rounded-md p-2 text-xs",children:n.details})]}),e.jsxs("div",{className:"text-muted-foreground flex flex-wrap items-center gap-2 text-xs",children:[e.jsx(k,{variant:"outline",className:"text-xs",children:h}),s.author&&e.jsx("span",{children:s.author})]})]}),e.jsx("div",{className:"pt-0.5",children:e.jsx(S,{checked:x,onCheckedChange:p=>u(o.id,p),disabled:!j||c,"aria-label":`${x?"Disable":"Enable"} ${s.name}`})})]})})}function A(){const{data:o=[],isLoading:u}=w(),c=y(),s=C(),t=T(),h=new Set([...c.variables?[c.variables]:[],...s.variables?[s.variables]:[]]);async function n(a,x){(x?c:s).mutate(a,{onSuccess:()=>{m.info("Extension changed — reload the page to apply",{action:{label:"Reload now",onClick:()=>location.reload()}})},onError:l=>{const i=x?"enable":"disable";m.error(`Failed to ${i} extension: ${l.message}`)}})}function r(){t.mutate(void 0,{onSuccess:a=>{m.success(`Reloaded ${a.length} extension(s)`)},onError:()=>{m.error("Failed to reload extensions")}})}return u?e.jsx("div",{className:"text-muted-foreground py-8 text-center text-sm",children:"Loading extensions…"}):e.jsxs("div",{className:"space-y-4",children:[e.jsxs("p",{className:"text-muted-foreground text-sm",children:["Extensions add new UI and capabilities to DorkOS. Place them in"," ",e.jsx("code",{className:"bg-muted rounded px-1",children:"~/.dork/extensions/"})," or"," ",e.jsx("code",{className:"bg-muted rounded px-1",children:".dork/extensions/"})," in your project."]}),o.length===0?e.jsx("div",{className:"text-muted-foreground py-8 text-center text-sm","data-testid":"no-extensions",children:e.jsx("p",{children:"No extensions installed."})}):e.jsx("div",{className:"space-y-3",children:o.map(a=>e.jsx(I,{extension:a,onToggle:n,isToggling:h.has(a.id)},a.id))}),e.jsx("div",{className:"flex justify-end",children:e.jsxs(R,{variant:"outline",size:"sm",onClick:r,disabled:t.isPending,"data-testid":"reload-extensions-button",children:[e.jsx(z,{className:g("mr-2 size-4",t.isPending&&"animate-spin")}),"Reload Extensions"]})})]})}export{M as ExtensionLoader,_ as ExtensionProvider,A as ExtensionsSettingsTab,H as createExtensionAPI,B as useExtensions};