clauder-hub 0.1.15 → 0.1.16
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/package.json +1 -1
- package/server/dist/index.js +72 -13
- package/web/dist/assets/{MonacoEditor-CbpiSq9q.js → MonacoEditor-rtQRWVYN.js} +3 -3
- package/web/dist/assets/{cssMode-CN9bc7nj.js → cssMode-n1rt6kgo.js} +1 -1
- package/web/dist/assets/{freemarker2-CRnvTAds.js → freemarker2-wNWHsgjv.js} +1 -1
- package/web/dist/assets/{handlebars-C2kgmAWI.js → handlebars-DFf8L9Df.js} +1 -1
- package/web/dist/assets/{html-BDYl1She.js → html-CrDTS1ce.js} +1 -1
- package/web/dist/assets/{htmlMode-Cx5zCPn6.js → htmlMode-DY2rVVP8.js} +1 -1
- package/web/dist/assets/{index-zwtldSlq.js → index-BeiyXxTM.js} +63 -63
- package/web/dist/assets/{index-m9eVnt__.js → index-DjilKjML.js} +1 -1
- package/web/dist/assets/index-mLv9JUIK.css +32 -0
- package/web/dist/assets/{javascript-vSl2Hjmn.js → javascript-7XefT4TE.js} +1 -1
- package/web/dist/assets/{jsonMode-DzNsYGk_.js → jsonMode-CbGU4wwk.js} +1 -1
- package/web/dist/assets/{liquid-kBEtoK3z.js → liquid-Bzc0sREQ.js} +1 -1
- package/web/dist/assets/{lspLanguageFeatures-FKrlDCvN.js → lspLanguageFeatures-BHAUOAfW.js} +1 -1
- package/web/dist/assets/{mdx-DlYho8un.js → mdx-CWLpB9fb.js} +1 -1
- package/web/dist/assets/{python-CscaxAmN.js → python-B5FO5i6L.js} +1 -1
- package/web/dist/assets/{razor-ML1yBys3.js → razor-CWWty3wH.js} +1 -1
- package/web/dist/assets/{tsMode-BMrsa7Gm.js → tsMode-BjWNTcOJ.js} +1 -1
- package/web/dist/assets/{typescript-Cs2X5p1b.js → typescript-DPbJAIdT.js} +1 -1
- package/web/dist/assets/{workers-B5NJGWxk.js → workers-DV0Cayx_.js} +1 -1
- package/web/dist/assets/{xml-CDgfErGy.js → xml-CaOpHlDM.js} +1 -1
- package/web/dist/assets/{yaml-Pb8QFqoF.js → yaml-BirwSaTi.js} +1 -1
- package/web/dist/index.html +2 -2
- package/web/dist/assets/index-R-juGYqF.css +0 -32
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "clauder-hub",
|
|
3
|
-
"version": "0.1.
|
|
3
|
+
"version": "0.1.16",
|
|
4
4
|
"description": "A local dashboard that runs, watches and automates the Claude Code sessions of every project on the machine",
|
|
5
5
|
"license": "MIT",
|
|
6
6
|
"author": "goldyndevs <goldyndevs@gmail.com>",
|
package/server/dist/index.js
CHANGED
|
@@ -1389,7 +1389,7 @@ var require_ms = __commonJS({
|
|
|
1389
1389
|
var require_debug = __commonJS({
|
|
1390
1390
|
"node_modules/debug/src/debug.js"(exports, module) {
|
|
1391
1391
|
exports = module.exports = createDebug.debug = createDebug["default"] = createDebug;
|
|
1392
|
-
exports.coerce =
|
|
1392
|
+
exports.coerce = coerce2;
|
|
1393
1393
|
exports.disable = disable;
|
|
1394
1394
|
exports.enable = enable;
|
|
1395
1395
|
exports.enabled = enabled;
|
|
@@ -1483,7 +1483,7 @@ var require_debug = __commonJS({
|
|
|
1483
1483
|
}
|
|
1484
1484
|
return false;
|
|
1485
1485
|
}
|
|
1486
|
-
function
|
|
1486
|
+
function coerce2(val) {
|
|
1487
1487
|
if (val instanceof Error) return val.stack || val.message;
|
|
1488
1488
|
return val;
|
|
1489
1489
|
}
|
|
@@ -27216,6 +27216,8 @@ CREATE INDEX IF NOT EXISTS idx_runs_status ON runs(status, created_at);
|
|
|
27216
27216
|
ensureColumn("channel_threads", "project_id", "TEXT");
|
|
27217
27217
|
ensureColumn("runs", "resume", "INTEGER NOT NULL DEFAULT 0");
|
|
27218
27218
|
ensureColumn("runs", "queued_at", "TEXT");
|
|
27219
|
+
ensureColumn("jobs", "params", "TEXT");
|
|
27220
|
+
ensureColumn("runs", "params", "TEXT");
|
|
27219
27221
|
}
|
|
27220
27222
|
function now() {
|
|
27221
27223
|
return (/* @__PURE__ */ new Date()).toISOString();
|
|
@@ -36469,7 +36471,7 @@ function currentValue(purpose) {
|
|
|
36469
36471
|
switch (purpose.kind) {
|
|
36470
36472
|
case "edit-job": {
|
|
36471
36473
|
const job = getJob(purpose.targetId);
|
|
36472
|
-
return job && { name: job.name, kind: job.kind, schedule: job.schedule, prompt: job.prompt, category: job.category, presetId: job.presetId, enabled: job.enabled };
|
|
36474
|
+
return job && { name: job.name, kind: job.kind, schedule: job.schedule, prompt: job.prompt, category: job.category, presetId: job.presetId, params: job.params, enabled: job.enabled };
|
|
36473
36475
|
}
|
|
36474
36476
|
case "edit-category": {
|
|
36475
36477
|
const category = listCategories().find((item) => item.id === purpose.targetId);
|
|
@@ -36571,6 +36573,13 @@ var init_purposes = __esm({
|
|
|
36571
36573
|
},
|
|
36572
36574
|
{ name: "category", type: "string", required: false, description: '"auto" (Claude picks) or a task category name; decides what the run must deliver before it closes. Default "auto".', example: "bugfix" },
|
|
36573
36575
|
{ name: "presetId", type: "string | null", required: false, description: "Toolset preset id from GET /api/presets; null uses the default preset.", example: "null" },
|
|
36576
|
+
{
|
|
36577
|
+
name: "params",
|
|
36578
|
+
type: "array",
|
|
36579
|
+
required: false,
|
|
36580
|
+
description: 'Values the prompt takes as {{name}}: [{ name, label?, type: "number" | "string" | "boolean", default, description? }]. Run now asks for them (defaults filled in), cron runs use the defaults, POST /api/jobs/<id>/run and the webhook take {"params": {...}}; a wrong type is refused. Left out on a PUT, the job keeps its params; [] clears them.',
|
|
36581
|
+
example: '[{ "name": "count", "label": "Episodes", "type": "number", "default": 5 }]'
|
|
36582
|
+
},
|
|
36574
36583
|
{ name: "enabled", type: "boolean", required: false, description: "Default true.", example: "true" }
|
|
36575
36584
|
];
|
|
36576
36585
|
CATEGORY_PARAMS = [
|
|
@@ -36872,6 +36881,47 @@ function getJob(id) {
|
|
|
36872
36881
|
const row = db.prepare("SELECT * FROM jobs WHERE id = ?").get(id);
|
|
36873
36882
|
return row ? toJob(row) : null;
|
|
36874
36883
|
}
|
|
36884
|
+
function checkParams(input) {
|
|
36885
|
+
if (input == null) return [];
|
|
36886
|
+
if (!Array.isArray(input)) throw new Error("params is a list of { name, label?, type, default, description? }.");
|
|
36887
|
+
const seen = /* @__PURE__ */ new Set();
|
|
36888
|
+
return input.map((item) => {
|
|
36889
|
+
const name = String(item?.name ?? "").trim();
|
|
36890
|
+
if (!/^[A-Za-z_]\w*$/.test(name)) throw new Error(`Parameter name '${name}' must be letters, digits and _, not starting with a digit.`);
|
|
36891
|
+
if (RESERVED2.includes(name)) throw new Error(`Parameter name '${name}' is reserved.`);
|
|
36892
|
+
if (seen.has(name)) throw new Error(`Parameter '${name}' is listed twice.`);
|
|
36893
|
+
seen.add(name);
|
|
36894
|
+
if (!PARAM_TYPES.includes(item.type)) throw new Error(`Parameter '${name}': type is number, string or boolean.`);
|
|
36895
|
+
const fallback = item.type === "number" ? 0 : item.type === "boolean" ? false : "";
|
|
36896
|
+
const param = { name, type: item.type, default: coerce({ name, type: item.type }, item.default ?? fallback) };
|
|
36897
|
+
if (item.label) param.label = String(item.label);
|
|
36898
|
+
if (item.description) param.description = String(item.description);
|
|
36899
|
+
return param;
|
|
36900
|
+
});
|
|
36901
|
+
}
|
|
36902
|
+
function coerce(param, value) {
|
|
36903
|
+
if (param.type === "number") {
|
|
36904
|
+
const number = typeof value === "number" ? value : typeof value === "string" && value.trim() !== "" ? Number(value) : NaN;
|
|
36905
|
+
if (!Number.isFinite(number)) throw new Error(`Parameter '${param.name}' must be a number, got ${JSON.stringify(value)}.`);
|
|
36906
|
+
return number;
|
|
36907
|
+
}
|
|
36908
|
+
if (param.type === "boolean") {
|
|
36909
|
+
if (value === true || value === "true") return true;
|
|
36910
|
+
if (value === false || value === "false") return false;
|
|
36911
|
+
throw new Error(`Parameter '${param.name}' must be true or false, got ${JSON.stringify(value)}.`);
|
|
36912
|
+
}
|
|
36913
|
+
if (typeof value === "object" && value !== null) throw new Error(`Parameter '${param.name}' must be a string.`);
|
|
36914
|
+
return String(value);
|
|
36915
|
+
}
|
|
36916
|
+
function resolveParams(job, given) {
|
|
36917
|
+
if (given != null && (typeof given !== "object" || Array.isArray(given))) throw new Error("params is an object of name: value.");
|
|
36918
|
+
const values = given ?? {};
|
|
36919
|
+
const unknown = Object.keys(values).filter((name) => !job.params.some((param) => param.name === name));
|
|
36920
|
+
if (unknown.length) throw new Error(`Unknown parameter${unknown.length > 1 ? "s" : ""}: ${unknown.join(", ")}. This job takes: ${job.params.map((p2) => p2.name).join(", ") || "none"}.`);
|
|
36921
|
+
const result = {};
|
|
36922
|
+
for (const param of job.params) result[param.name] = values[param.name] == null ? param.default : coerce(param, values[param.name]);
|
|
36923
|
+
return result;
|
|
36924
|
+
}
|
|
36875
36925
|
function saveJob(input) {
|
|
36876
36926
|
if (!input.name.trim()) throw new Error("A job needs a name.");
|
|
36877
36927
|
if (!input.prompt.trim()) throw new Error("A job needs a prompt.");
|
|
@@ -36885,12 +36935,14 @@ function saveJob(input) {
|
|
|
36885
36935
|
}
|
|
36886
36936
|
}
|
|
36887
36937
|
const id = input.id ?? nanoid(10);
|
|
36888
|
-
const
|
|
36938
|
+
const existing = getJob(id);
|
|
36939
|
+
const params = input.params === void 0 && existing ? existing.params : checkParams(input.params);
|
|
36940
|
+
const token = existing?.token ?? nanoid(32);
|
|
36889
36941
|
db.prepare(
|
|
36890
|
-
`INSERT INTO jobs (id, name, kind, schedule, prompt, category, preset_id, token, enabled, created_at, updated_at) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)
|
|
36942
|
+
`INSERT INTO jobs (id, name, kind, schedule, prompt, category, preset_id, params, token, enabled, created_at, updated_at) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)
|
|
36891
36943
|
ON CONFLICT(id) DO UPDATE SET name = excluded.name, kind = excluded.kind, schedule = excluded.schedule, prompt = excluded.prompt,
|
|
36892
|
-
category = excluded.category, preset_id = excluded.preset_id, enabled = excluded.enabled, updated_at = excluded.updated_at`
|
|
36893
|
-
).run(id, input.name.trim(), input.kind, schedule, input.prompt, input.category ?? "auto", input.presetId || null, token, input.enabled === false ? 0 : 1, now(), now());
|
|
36944
|
+
category = excluded.category, preset_id = excluded.preset_id, params = excluded.params, enabled = excluded.enabled, updated_at = excluded.updated_at`
|
|
36945
|
+
).run(id, input.name.trim(), input.kind, schedule, input.prompt, input.category ?? "auto", input.presetId || null, JSON.stringify(params), token, input.enabled === false ? 0 : 1, now(), now());
|
|
36894
36946
|
schedule_(id);
|
|
36895
36947
|
broadcast("jobs", {});
|
|
36896
36948
|
return getJob(id);
|
|
@@ -36935,8 +36987,9 @@ function stopScheduler() {
|
|
|
36935
36987
|
crons.delete(key);
|
|
36936
36988
|
}
|
|
36937
36989
|
}
|
|
36938
|
-
function renderJobPrompt(template, payload) {
|
|
36990
|
+
function renderJobPrompt(template, payload, params) {
|
|
36939
36991
|
const values = { now: now(), payload: JSON.stringify(payload ?? {}, null, 2) };
|
|
36992
|
+
for (const [name, value] of Object.entries(params)) values[name] = String(value);
|
|
36940
36993
|
const walk = (value, prefix) => {
|
|
36941
36994
|
if (value && typeof value === "object" && !Array.isArray(value)) {
|
|
36942
36995
|
for (const [key, inner] of Object.entries(value)) walk(inner, `${prefix}.${key}`);
|
|
@@ -36950,15 +37003,17 @@ function listRuns(limit = 100) {
|
|
|
36950
37003
|
return db.prepare("SELECT runs.*, jobs.name AS job_name FROM runs LEFT JOIN jobs ON jobs.id = runs.job_id ORDER BY runs.created_at DESC LIMIT ?").all(limit).map(toRun);
|
|
36951
37004
|
}
|
|
36952
37005
|
function enqueue(input) {
|
|
36953
|
-
const
|
|
37006
|
+
const params = input.job ? resolveParams(input.job, input.params) : {};
|
|
37007
|
+
const prompt = input.job ? renderJobPrompt(input.job.prompt, input.payload, params) : input.prompt ?? "";
|
|
36954
37008
|
if (!prompt.trim()) throw new Error("Nothing to run: the prompt is empty.");
|
|
36955
37009
|
const id = nanoid(10);
|
|
36956
|
-
db.prepare("INSERT INTO runs (id, job_id, source, prompt, category, status, created_at) VALUES (?, ?, ?, ?, ?, 'queued', ?)").run(
|
|
37010
|
+
db.prepare("INSERT INTO runs (id, job_id, source, prompt, category, status, params, created_at) VALUES (?, ?, ?, ?, ?, 'queued', ?, ?)").run(
|
|
36957
37011
|
id,
|
|
36958
37012
|
input.job?.id ?? null,
|
|
36959
37013
|
input.source,
|
|
36960
37014
|
prompt,
|
|
36961
37015
|
input.category ?? input.job?.category ?? "auto",
|
|
37016
|
+
Object.keys(params).length ? JSON.stringify(params) : null,
|
|
36962
37017
|
now()
|
|
36963
37018
|
);
|
|
36964
37019
|
if (input.job) db.prepare("UPDATE jobs SET last_run_at = ? WHERE id = ?").run(now(), input.job.id);
|
|
@@ -37073,7 +37128,7 @@ function recoverRuns() {
|
|
|
37073
37128
|
db.prepare("UPDATE interactions SET status = 'closed', answered_by = 'hub restart' WHERE status = 'open'").run();
|
|
37074
37129
|
return stranded;
|
|
37075
37130
|
}
|
|
37076
|
-
var crons, cronKey, toJob, toRun, IN_LINE, advancing, waitingForClaude;
|
|
37131
|
+
var crons, cronKey, toJob, PARAM_TYPES, RESERVED2, toRun, IN_LINE, advancing, waitingForClaude;
|
|
37077
37132
|
var init_jobs = __esm({
|
|
37078
37133
|
"server/src/jobs.ts"() {
|
|
37079
37134
|
"use strict";
|
|
@@ -37098,12 +37153,15 @@ var init_jobs = __esm({
|
|
|
37098
37153
|
prompt: row.prompt,
|
|
37099
37154
|
category: row.category,
|
|
37100
37155
|
presetId: row.preset_id,
|
|
37156
|
+
params: parseJson(row.params, []),
|
|
37101
37157
|
token: row.token,
|
|
37102
37158
|
enabled: Boolean(row.enabled),
|
|
37103
37159
|
lastRunAt: row.last_run_at,
|
|
37104
37160
|
nextRunAt: crons.get(cronKey(row.id))?.nextRun()?.toISOString() ?? null,
|
|
37105
37161
|
createdAt: row.created_at
|
|
37106
37162
|
});
|
|
37163
|
+
PARAM_TYPES = ["number", "string", "boolean"];
|
|
37164
|
+
RESERVED2 = ["now", "payload"];
|
|
37107
37165
|
toRun = (row) => ({
|
|
37108
37166
|
id: row.id,
|
|
37109
37167
|
jobId: row.job_id,
|
|
@@ -37114,6 +37172,7 @@ var init_jobs = __esm({
|
|
|
37114
37172
|
status: row.status,
|
|
37115
37173
|
sessionId: row.session_id,
|
|
37116
37174
|
resume: Boolean(row.resume),
|
|
37175
|
+
params: parseJson(row.params, null),
|
|
37117
37176
|
createdAt: row.created_at,
|
|
37118
37177
|
startedAt: row.started_at,
|
|
37119
37178
|
finishedAt: row.finished_at
|
|
@@ -38176,7 +38235,7 @@ api.post(
|
|
|
38176
38235
|
wrap2((req) => {
|
|
38177
38236
|
const job = getJob(req.params.id);
|
|
38178
38237
|
if (!job) throw new Error("No such job.");
|
|
38179
|
-
return enqueue({ job, source: "manual", payload: req.body?.payload ?? {} });
|
|
38238
|
+
return enqueue({ job, source: "manual", payload: req.body?.payload ?? {}, params: req.body?.params });
|
|
38180
38239
|
})
|
|
38181
38240
|
);
|
|
38182
38241
|
api.post(
|
|
@@ -38192,7 +38251,7 @@ api.post(
|
|
|
38192
38251
|
res.status(409).json({ error: "This trigger is disabled." });
|
|
38193
38252
|
return;
|
|
38194
38253
|
}
|
|
38195
|
-
const { run: run4, position } = enqueue({ job, source: "webhook", payload: req.body ?? {} });
|
|
38254
|
+
const { run: run4, position } = enqueue({ job, source: "webhook", payload: req.body ?? {}, params: req.body?.params });
|
|
38196
38255
|
return { runId: run4.id, status: run4.status, position };
|
|
38197
38256
|
})
|
|
38198
38257
|
);
|