clankie 0.8.0 → 0.10.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 +3 -0
- package/dist/cli.js +305 -39
- package/package.json +3 -2
- package/skills/clankie-admin/SKILL.md +64 -0
- package/web-ui-dist/_shell.html +2 -2
- package/web-ui-dist/assets/{auth-CszIWbjg.js → auth-B20mIC_p.js} +1 -1
- package/web-ui-dist/assets/badge-3e57zy_2.js +1 -0
- package/web-ui-dist/assets/check-CePvKusa.js +1 -0
- package/web-ui-dist/assets/{circle-x-8a_H31zB.js → circle-x-B1Pwi07a.js} +1 -1
- package/web-ui-dist/assets/{connection-CJ5rxoTn.js → connection-D4rgB5k2.js} +1 -1
- package/web-ui-dist/assets/extensions-BxWXmCbJ.js +1 -0
- package/web-ui-dist/assets/extensions-Cf8QLmLt.js +1 -0
- package/web-ui-dist/assets/{field-B65zDnKR.js → field-DfBj0pPw.js} +1 -1
- package/web-ui-dist/assets/{index-DagqCCaE.js → index-Ff5WtXhh.js} +1 -1
- package/web-ui-dist/assets/{index-BPe4bC0P.js → index-TBNB5eLy.js} +1 -1
- package/web-ui-dist/assets/{json-render-renderer-LfYi54yN.js → json-render-renderer-BrlU1n47.js} +1 -1
- package/web-ui-dist/assets/main-CP6prmzV.js +35 -0
- package/web-ui-dist/assets/{scoped-models-Dn75_0eE.js → scoped-models-DDH_ssLY.js} +1 -1
- package/web-ui-dist/assets/{sessions._sessionId-BMqy9VwK.js → sessions._sessionId-CWtQlITL.js} +24 -24
- package/web-ui-dist/assets/{skills-DZkToxg_.js → skills-Clk3tV2m.js} +1 -1
- package/web-ui-dist/assets/styles-KEhqa3CU.css +1 -0
- package/web-ui-dist/assets/{theme-g799ir3H.js → theme-e79Jvep_.js} +1 -1
- package/web-ui-dist/assets/badge-n982dtcJ.js +0 -1
- package/web-ui-dist/assets/check-CFMzbw3W.js +0 -1
- package/web-ui-dist/assets/extensions-C5MIyE-l.js +0 -1
- package/web-ui-dist/assets/extensions-CnpFPAZe.js +0 -1
- package/web-ui-dist/assets/main-DLbCrY1P.js +0 -35
- package/web-ui-dist/assets/styles-BsoKVYca.css +0 -1
package/README.md
CHANGED
package/dist/cli.js
CHANGED
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
2
|
import { createRequire } from "node:module";
|
|
3
|
+
import { execSync, spawn, spawnSync } from "node:child_process";
|
|
3
4
|
import * as crypto$2 from "node:crypto";
|
|
4
5
|
import { createHash, randomBytes, randomUUID } from "node:crypto";
|
|
5
6
|
import * as fs$6 from "node:fs";
|
|
@@ -14,7 +15,7 @@ import path, { basename as basename$1, dirname as dirname$1, isAbsolute as isAbs
|
|
|
14
15
|
import { fileURLToPath } from "url";
|
|
15
16
|
import * as os$2 from "node:os";
|
|
16
17
|
import os, { homedir as homedir$1, platform as platform$1, tmpdir } from "node:os";
|
|
17
|
-
import { exec, execSync, spawn, spawnSync } from "child_process";
|
|
18
|
+
import { exec, execSync as execSync$1, spawn as spawn$1, spawnSync as spawnSync$1 } from "child_process";
|
|
18
19
|
import { Readable } from "stream";
|
|
19
20
|
import crypto$1, { randomUUID as randomUUID$1 } from "crypto";
|
|
20
21
|
import { createServer } from "http";
|
|
@@ -36,7 +37,6 @@ import { access as access$1, lstat, open, readFile as readFile$1, readdir as rea
|
|
|
36
37
|
import { EventEmitter as EventEmitter$1 } from "node:events";
|
|
37
38
|
import tty from "node:tty";
|
|
38
39
|
import { createRequire as createRequire$1 } from "module";
|
|
39
|
-
import { execSync as execSync$1, spawn as spawn$1, spawnSync as spawnSync$1 } from "node:child_process";
|
|
40
40
|
import { StringDecoder } from "node:string_decoder";
|
|
41
41
|
import { finished as finished$1 } from "stream/promises";
|
|
42
42
|
import { createInterface } from "node:readline";
|
|
@@ -213478,7 +213478,7 @@ let cachedShellConfig = null;
|
|
|
213478
213478
|
function findBashOnPath() {
|
|
213479
213479
|
if (process.platform === "win32") {
|
|
213480
213480
|
try {
|
|
213481
|
-
const result = spawnSync("where", ["bash.exe"], {
|
|
213481
|
+
const result = spawnSync$1("where", ["bash.exe"], {
|
|
213482
213482
|
encoding: "utf-8",
|
|
213483
213483
|
timeout: 5e3
|
|
213484
213484
|
});
|
|
@@ -213490,7 +213490,7 @@ function findBashOnPath() {
|
|
|
213490
213490
|
return null;
|
|
213491
213491
|
}
|
|
213492
213492
|
try {
|
|
213493
|
-
const result = spawnSync("which", ["bash"], {
|
|
213493
|
+
const result = spawnSync$1("which", ["bash"], {
|
|
213494
213494
|
encoding: "utf-8",
|
|
213495
213495
|
timeout: 5e3
|
|
213496
213496
|
});
|
|
@@ -213601,7 +213601,7 @@ function sanitizeBinaryOutput(str) {
|
|
|
213601
213601
|
*/
|
|
213602
213602
|
function killProcessTree(pid) {
|
|
213603
213603
|
if (process.platform === "win32") try {
|
|
213604
|
-
spawn("taskkill", [
|
|
213604
|
+
spawn$1("taskkill", [
|
|
213605
213605
|
"/F",
|
|
213606
213606
|
"/T",
|
|
213607
213607
|
"/PID",
|
|
@@ -213822,7 +213822,7 @@ function truncateLine(line, maxChars = 500) {
|
|
|
213822
213822
|
function executeBash(command, options) {
|
|
213823
213823
|
return new Promise((resolve, reject) => {
|
|
213824
213824
|
const { shell, args } = getShellConfig();
|
|
213825
|
-
const child = spawn(shell, [...args, command], {
|
|
213825
|
+
const child = spawn$1(shell, [...args, command], {
|
|
213826
213826
|
detached: true,
|
|
213827
213827
|
env: getShellEnv(),
|
|
213828
213828
|
stdio: [
|
|
@@ -263025,7 +263025,7 @@ function walkDirectoryWithFd(baseDir, fdPath, query, maxResults) {
|
|
|
263025
263025
|
".git/**"
|
|
263026
263026
|
];
|
|
263027
263027
|
if (query) args.push(query);
|
|
263028
|
-
const result = spawnSync(fdPath, args, {
|
|
263028
|
+
const result = spawnSync$1(fdPath, args, {
|
|
263029
263029
|
encoding: "utf-8",
|
|
263030
263030
|
stdio: [
|
|
263031
263031
|
"pipe",
|
|
@@ -271480,7 +271480,7 @@ function createEventBus() {
|
|
|
271480
271480
|
*/
|
|
271481
271481
|
async function execCommand(command, args, cwd, options) {
|
|
271482
271482
|
return new Promise((resolve) => {
|
|
271483
|
-
const proc = spawn
|
|
271483
|
+
const proc = spawn(command, args, {
|
|
271484
271484
|
cwd,
|
|
271485
271485
|
shell: false,
|
|
271486
271486
|
stdio: [
|
|
@@ -273542,7 +273542,7 @@ const defaultBashOperations = { exec: (command, cwd, { onData, signal, timeout,
|
|
|
273542
273542
|
reject(/* @__PURE__ */ new Error(`Working directory does not exist: ${cwd}\nCannot execute bash commands.`));
|
|
273543
273543
|
return;
|
|
273544
273544
|
}
|
|
273545
|
-
const child = spawn(shell, [...args, command], {
|
|
273545
|
+
const child = spawn$1(shell, [...args, command], {
|
|
273546
273546
|
cwd,
|
|
273547
273547
|
detached: true,
|
|
273548
273548
|
env: env ?? getShellEnv(),
|
|
@@ -279228,7 +279228,7 @@ const TOOLS = {
|
|
|
279228
279228
|
};
|
|
279229
279229
|
function commandExists(cmd) {
|
|
279230
279230
|
try {
|
|
279231
|
-
const result = spawnSync(cmd, ["--version"], { stdio: "pipe" });
|
|
279231
|
+
const result = spawnSync$1(cmd, ["--version"], { stdio: "pipe" });
|
|
279232
279232
|
return result.error === void 0 || result.error === null;
|
|
279233
279233
|
} catch {
|
|
279234
279234
|
return false;
|
|
@@ -279289,7 +279289,7 @@ async function downloadTool(tool) {
|
|
|
279289
279289
|
mkdirSync$1(extractDir, { recursive: true });
|
|
279290
279290
|
try {
|
|
279291
279291
|
if (assetName.endsWith(".tar.gz")) {
|
|
279292
|
-
const extractResult = spawnSync("tar", [
|
|
279292
|
+
const extractResult = spawnSync$1("tar", [
|
|
279293
279293
|
"xzf",
|
|
279294
279294
|
archivePath,
|
|
279295
279295
|
"-C",
|
|
@@ -279451,7 +279451,7 @@ function createFindTool(cwd, options) {
|
|
|
279451
279451
|
} catch {}
|
|
279452
279452
|
for (const gitignorePath of gitignoreFiles) args.push("--ignore-file", gitignorePath);
|
|
279453
279453
|
args.push(pattern, searchPath);
|
|
279454
|
-
const result = spawnSync(fdPath, args, {
|
|
279454
|
+
const result = spawnSync$1(fdPath, args, {
|
|
279455
279455
|
encoding: "utf-8",
|
|
279456
279456
|
maxBuffer: 10 * 1024 * 1024
|
|
279457
279457
|
});
|
|
@@ -279606,7 +279606,7 @@ function createGrepTool(cwd, options) {
|
|
|
279606
279606
|
if (literal) args.push("--fixed-strings");
|
|
279607
279607
|
if (glob) args.push("--glob", glob);
|
|
279608
279608
|
args.push(pattern, searchPath);
|
|
279609
|
-
const child = spawn(rgPath, args, { stdio: [
|
|
279609
|
+
const child = spawn$1(rgPath, args, { stdio: [
|
|
279610
279610
|
"ignore",
|
|
279611
279611
|
"pipe",
|
|
279612
279612
|
"pipe"
|
|
@@ -285304,7 +285304,7 @@ function executeCommand(commandConfig) {
|
|
|
285304
285304
|
const command = commandConfig.slice(1);
|
|
285305
285305
|
let result;
|
|
285306
285306
|
try {
|
|
285307
|
-
result = execSync(command, {
|
|
285307
|
+
result = execSync$1(command, {
|
|
285308
285308
|
encoding: "utf-8",
|
|
285309
285309
|
timeout: 1e4,
|
|
285310
285310
|
stdio: [
|
|
@@ -289898,7 +289898,7 @@ var DefaultPackageManager = class {
|
|
|
289898
289898
|
}
|
|
289899
289899
|
runCommand(command, args, options) {
|
|
289900
289900
|
return new Promise((resolvePromise, reject) => {
|
|
289901
|
-
const child = spawn
|
|
289901
|
+
const child = spawn(command, args, {
|
|
289902
289902
|
cwd: options?.cwd,
|
|
289903
289903
|
stdio: "inherit",
|
|
289904
289904
|
shell: process.platform === "win32"
|
|
@@ -289911,7 +289911,7 @@ var DefaultPackageManager = class {
|
|
|
289911
289911
|
});
|
|
289912
289912
|
}
|
|
289913
289913
|
runCommandSync(command, args) {
|
|
289914
|
-
const result = spawnSync
|
|
289914
|
+
const result = spawnSync(command, args, {
|
|
289915
289915
|
stdio: [
|
|
289916
289916
|
"ignore",
|
|
289917
289917
|
"pipe",
|
|
@@ -292560,7 +292560,7 @@ var SessionList = class {
|
|
|
292560
292560
|
* Delete a session file, trying the `trash` CLI first, then falling back to unlink
|
|
292561
292561
|
*/
|
|
292562
292562
|
async function deleteSessionFile(sessionPath) {
|
|
292563
|
-
const trashResult = spawnSync
|
|
292563
|
+
const trashResult = spawnSync("trash", sessionPath.startsWith("-") ? ["--", sessionPath] : [sessionPath], { encoding: "utf-8" });
|
|
292564
292564
|
const getTrashErrorHint = () => {
|
|
292565
292565
|
const parts = [];
|
|
292566
292566
|
if (trashResult.error) parts.push(trashResult.error.message);
|
|
@@ -293321,7 +293321,7 @@ async function convertToPng$1(bytes) {
|
|
|
293321
293321
|
}
|
|
293322
293322
|
}
|
|
293323
293323
|
function runCommand(command, args, options) {
|
|
293324
|
-
const result = spawnSync(command, args, {
|
|
293324
|
+
const result = spawnSync$1(command, args, {
|
|
293325
293325
|
timeout: options?.timeoutMs ?? DEFAULT_READ_TIMEOUT_MS,
|
|
293326
293326
|
maxBuffer: options?.maxBufferBytes ?? DEFAULT_MAX_BUFFER_BYTES
|
|
293327
293327
|
});
|
|
@@ -293418,16 +293418,16 @@ function copyToClipboard(text) {
|
|
|
293418
293418
|
timeout: 5e3
|
|
293419
293419
|
};
|
|
293420
293420
|
try {
|
|
293421
|
-
if (p === "darwin") execSync("pbcopy", options);
|
|
293422
|
-
else if (p === "win32") execSync("clip", options);
|
|
293421
|
+
if (p === "darwin") execSync$1("pbcopy", options);
|
|
293422
|
+
else if (p === "win32") execSync$1("clip", options);
|
|
293423
293423
|
else {
|
|
293424
293424
|
if (process.env.TERMUX_VERSION) try {
|
|
293425
|
-
execSync("termux-clipboard-set", options);
|
|
293425
|
+
execSync$1("termux-clipboard-set", options);
|
|
293426
293426
|
return;
|
|
293427
293427
|
} catch {}
|
|
293428
293428
|
if (isWaylandSession()) try {
|
|
293429
|
-
execSync("which wl-copy", { stdio: "ignore" });
|
|
293430
|
-
const proc = spawn("wl-copy", [], { stdio: [
|
|
293429
|
+
execSync$1("which wl-copy", { stdio: "ignore" });
|
|
293430
|
+
const proc = spawn$1("wl-copy", [], { stdio: [
|
|
293431
293431
|
"pipe",
|
|
293432
293432
|
"ignore",
|
|
293433
293433
|
"ignore"
|
|
@@ -293438,15 +293438,15 @@ function copyToClipboard(text) {
|
|
|
293438
293438
|
proc.unref();
|
|
293439
293439
|
} catch {
|
|
293440
293440
|
try {
|
|
293441
|
-
execSync("xclip -selection clipboard", options);
|
|
293441
|
+
execSync$1("xclip -selection clipboard", options);
|
|
293442
293442
|
} catch {
|
|
293443
|
-
execSync("xsel --clipboard --input", options);
|
|
293443
|
+
execSync$1("xsel --clipboard --input", options);
|
|
293444
293444
|
}
|
|
293445
293445
|
}
|
|
293446
293446
|
else try {
|
|
293447
|
-
execSync("xclip -selection clipboard", options);
|
|
293447
|
+
execSync$1("xclip -selection clipboard", options);
|
|
293448
293448
|
} catch {
|
|
293449
|
-
execSync("xsel --clipboard --input", options);
|
|
293449
|
+
execSync$1("xsel --clipboard --input", options);
|
|
293450
293450
|
}
|
|
293451
293451
|
}
|
|
293452
293452
|
} catch {}
|
|
@@ -294465,7 +294465,7 @@ var ExtensionEditorComponent = class extends Container {
|
|
|
294465
294465
|
fs$6.writeFileSync(tmpFile, currentText, "utf-8");
|
|
294466
294466
|
this.tui.stop();
|
|
294467
294467
|
const [editor, ...editorArgs] = editorCmd.split(" ");
|
|
294468
|
-
if (spawnSync
|
|
294468
|
+
if (spawnSync(editor, [...editorArgs, tmpFile], { stdio: "inherit" }).status === 0) {
|
|
294469
294469
|
const newContent = fs$6.readFileSync(tmpFile, "utf-8").replace(/\n$/, "");
|
|
294470
294470
|
this.editor.setText(newContent);
|
|
294471
294471
|
}
|
|
@@ -298986,7 +298986,7 @@ var InteractiveMode = class InteractiveMode {
|
|
|
298986
298986
|
fs$6.writeFileSync(tmpFile, currentText, "utf-8");
|
|
298987
298987
|
this.ui.stop();
|
|
298988
298988
|
const [editor, ...editorArgs] = editorCmd.split(" ");
|
|
298989
|
-
if (spawnSync(editor, [...editorArgs, tmpFile], { stdio: "inherit" }).status === 0) {
|
|
298989
|
+
if (spawnSync$1(editor, [...editorArgs, tmpFile], { stdio: "inherit" }).status === 0) {
|
|
298990
298990
|
const newContent = fs$6.readFileSync(tmpFile, "utf-8").replace(/\n$/, "");
|
|
298991
298991
|
this.editor.setText(newContent);
|
|
298992
298992
|
}
|
|
@@ -299749,7 +299749,7 @@ var InteractiveMode = class InteractiveMode {
|
|
|
299749
299749
|
}
|
|
299750
299750
|
async handleShareCommand() {
|
|
299751
299751
|
try {
|
|
299752
|
-
if (spawnSync("gh", ["auth", "status"], { encoding: "utf-8" }).status !== 0) {
|
|
299752
|
+
if (spawnSync$1("gh", ["auth", "status"], { encoding: "utf-8" }).status !== 0) {
|
|
299753
299753
|
this.showError("GitHub CLI is not logged in. Run 'gh auth login' first.");
|
|
299754
299754
|
return;
|
|
299755
299755
|
}
|
|
@@ -299786,7 +299786,7 @@ var InteractiveMode = class InteractiveMode {
|
|
|
299786
299786
|
};
|
|
299787
299787
|
try {
|
|
299788
299788
|
const result = await new Promise((resolve) => {
|
|
299789
|
-
proc = spawn("gh", [
|
|
299789
|
+
proc = spawn$1("gh", [
|
|
299790
299790
|
"gist",
|
|
299791
299791
|
"create",
|
|
299792
299792
|
"--public=false",
|
|
@@ -302538,14 +302538,14 @@ function parseSchedule(params) {
|
|
|
302538
302538
|
}
|
|
302539
302539
|
function createCronExtension() {
|
|
302540
302540
|
return function cronExtension(pi) {
|
|
302541
|
-
pi.on("before_agent_start", async () => {
|
|
302541
|
+
pi.on("before_agent_start", async (event) => {
|
|
302542
302542
|
const now = /* @__PURE__ */ new Date();
|
|
302543
302543
|
const timezone = Intl.DateTimeFormat().resolvedOptions().timeZone;
|
|
302544
302544
|
const local = now.toLocaleString("sv-SE", {
|
|
302545
302545
|
timeZone: timezone,
|
|
302546
302546
|
hour12: false
|
|
302547
302547
|
}).replace(" ", "T");
|
|
302548
|
-
return { systemPrompt:
|
|
302548
|
+
return { systemPrompt: `${event.systemPrompt}\n\nYou can manage scheduled tasks with the cron tool.\nCurrent UTC time: ${now.toISOString()}\nCurrent timezone: ${timezone} (local: ${local})` };
|
|
302549
302549
|
});
|
|
302550
302550
|
pi.registerTool({
|
|
302551
302551
|
name: "cron",
|
|
@@ -303414,6 +303414,10 @@ function createWorkspaceJailExtension(workspaceDir, allowedPaths = []) {
|
|
|
303414
303414
|
* This is defense-in-depth, not a complete sandbox.
|
|
303415
303415
|
*/
|
|
303416
303416
|
function scanBashCommand(command) {
|
|
303417
|
+
if (/(^|\s)(pi\s+(install|remove|update))(\s|$)/.test(command)) return {
|
|
303418
|
+
allowed: false,
|
|
303419
|
+
reason: "Blocked: use the manage_packages tool (or Settings → Extensions install UI) instead of running `pi install/remove/update` in bash."
|
|
303420
|
+
};
|
|
303417
303421
|
const absolutePathPattern = /(?:^|\s)([~/][\w\-./]+)/g;
|
|
303418
303422
|
let match;
|
|
303419
303423
|
while ((match = absolutePathPattern.exec(command)) !== null) {
|
|
@@ -303468,8 +303472,9 @@ function createWorkspaceJailExtension(workspaceDir, allowedPaths = []) {
|
|
|
303468
303472
|
}
|
|
303469
303473
|
}
|
|
303470
303474
|
});
|
|
303471
|
-
pi.on("before_agent_start", async () => {
|
|
303472
|
-
|
|
303475
|
+
pi.on("before_agent_start", async (event) => {
|
|
303476
|
+
const allowedPathsNote = normalizedAllowedPaths.length ? `\nAlso allowed: ${normalizedAllowedPaths.join(", ")}` : "";
|
|
303477
|
+
return { systemPrompt: `${event.systemPrompt}\n\nIMPORTANT: You are restricted to working within the directory: ${workspaceDir}${allowedPathsNote}
|
|
303473
303478
|
Do not access files, run commands, or reference paths outside the allowed directories.` };
|
|
303474
303479
|
});
|
|
303475
303480
|
};
|
|
@@ -303943,6 +303948,147 @@ function createHeartbeatExtension() {
|
|
|
303943
303948
|
};
|
|
303944
303949
|
}
|
|
303945
303950
|
//#endregion
|
|
303951
|
+
//#region src/extensions/package-manager.ts
|
|
303952
|
+
const PackageManagerParamsSchema = Type$1.Object({
|
|
303953
|
+
action: StringEnum([
|
|
303954
|
+
"install",
|
|
303955
|
+
"remove",
|
|
303956
|
+
"update",
|
|
303957
|
+
"list"
|
|
303958
|
+
]),
|
|
303959
|
+
source: Type$1.Optional(Type$1.String({ description: "Package source, e.g. npm:@scope/pkg, git:github.com/user/repo, or local path" })),
|
|
303960
|
+
scope: Type$1.Optional(StringEnum(["user", "project"]))
|
|
303961
|
+
});
|
|
303962
|
+
function stringifyPackageSource(source) {
|
|
303963
|
+
if (typeof source === "string") return source;
|
|
303964
|
+
return source.source;
|
|
303965
|
+
}
|
|
303966
|
+
function createPackageManagerExtension(reloadAllSessions) {
|
|
303967
|
+
return function packageManagerExtension(pi) {
|
|
303968
|
+
let pendingReload = false;
|
|
303969
|
+
pi.on("agent_end", () => {
|
|
303970
|
+
if (!pendingReload) return;
|
|
303971
|
+
pendingReload = false;
|
|
303972
|
+
setTimeout(() => {
|
|
303973
|
+
reloadAllSessions().catch((err) => {
|
|
303974
|
+
console.error("[package-manager] Failed to reload sessions:", err);
|
|
303975
|
+
});
|
|
303976
|
+
}, 0);
|
|
303977
|
+
});
|
|
303978
|
+
pi.registerTool({
|
|
303979
|
+
name: "manage_packages",
|
|
303980
|
+
label: "Manage Packages",
|
|
303981
|
+
description: "Install, remove, update, or list clankie packages in the correct clankie directories. Defaults to user scope.",
|
|
303982
|
+
parameters: PackageManagerParamsSchema,
|
|
303983
|
+
async execute(_toolCallId, rawParams) {
|
|
303984
|
+
const params = rawParams;
|
|
303985
|
+
const config = loadConfig();
|
|
303986
|
+
const cwd = getWorkspace(config);
|
|
303987
|
+
const agentDir = getAgentDir(config);
|
|
303988
|
+
const settingsManager = SettingsManager.create(cwd, agentDir);
|
|
303989
|
+
const packageManager = new DefaultPackageManager({
|
|
303990
|
+
cwd,
|
|
303991
|
+
agentDir,
|
|
303992
|
+
settingsManager
|
|
303993
|
+
});
|
|
303994
|
+
const output = [];
|
|
303995
|
+
packageManager.setProgressCallback((event) => {
|
|
303996
|
+
if (!event.message) return;
|
|
303997
|
+
output.push(event.message);
|
|
303998
|
+
});
|
|
303999
|
+
const scope = params.scope ?? "user";
|
|
304000
|
+
const local = scope === "project";
|
|
304001
|
+
const detailsBase = {
|
|
304002
|
+
action: params.action,
|
|
304003
|
+
scope,
|
|
304004
|
+
source: params.source?.trim() ?? null,
|
|
304005
|
+
output
|
|
304006
|
+
};
|
|
304007
|
+
try {
|
|
304008
|
+
switch (params.action) {
|
|
304009
|
+
case "list": {
|
|
304010
|
+
const globalPackages = (settingsManager.getGlobalSettings().packages ?? []).map(stringifyPackageSource);
|
|
304011
|
+
const projectPackages = (settingsManager.getProjectSettings().packages ?? []).map(stringifyPackageSource);
|
|
304012
|
+
return {
|
|
304013
|
+
content: [{
|
|
304014
|
+
type: "text",
|
|
304015
|
+
text: [
|
|
304016
|
+
"Configured package sources:",
|
|
304017
|
+
`- User (${globalPackages.length}): ${globalPackages.length > 0 ? globalPackages.join(", ") : "(none)"}`,
|
|
304018
|
+
`- Project (${projectPackages.length}): ${projectPackages.length > 0 ? projectPackages.join(", ") : "(none)"}`
|
|
304019
|
+
].join("\n")
|
|
304020
|
+
}],
|
|
304021
|
+
details: {
|
|
304022
|
+
...detailsBase,
|
|
304023
|
+
globalPackages,
|
|
304024
|
+
projectPackages
|
|
304025
|
+
}
|
|
304026
|
+
};
|
|
304027
|
+
}
|
|
304028
|
+
case "install": {
|
|
304029
|
+
if (!params.source?.trim()) throw new Error("install requires source");
|
|
304030
|
+
const source = params.source.trim();
|
|
304031
|
+
await packageManager.install(source, { local });
|
|
304032
|
+
packageManager.addSourceToSettings(source, { local });
|
|
304033
|
+
pendingReload = true;
|
|
304034
|
+
return {
|
|
304035
|
+
content: [{
|
|
304036
|
+
type: "text",
|
|
304037
|
+
text: output.join("\n") || `Installed ${source} in ${scope} scope and scheduled runtime reload.`
|
|
304038
|
+
}],
|
|
304039
|
+
details: {
|
|
304040
|
+
...detailsBase,
|
|
304041
|
+
source
|
|
304042
|
+
}
|
|
304043
|
+
};
|
|
304044
|
+
}
|
|
304045
|
+
case "remove": {
|
|
304046
|
+
if (!params.source?.trim()) throw new Error("remove requires source");
|
|
304047
|
+
const source = params.source.trim();
|
|
304048
|
+
await packageManager.remove(source, { local });
|
|
304049
|
+
packageManager.removeSourceFromSettings(source, { local });
|
|
304050
|
+
pendingReload = true;
|
|
304051
|
+
return {
|
|
304052
|
+
content: [{
|
|
304053
|
+
type: "text",
|
|
304054
|
+
text: output.join("\n") || `Removed ${source} from ${scope} scope and scheduled runtime reload.`
|
|
304055
|
+
}],
|
|
304056
|
+
details: {
|
|
304057
|
+
...detailsBase,
|
|
304058
|
+
source
|
|
304059
|
+
}
|
|
304060
|
+
};
|
|
304061
|
+
}
|
|
304062
|
+
case "update": {
|
|
304063
|
+
const source = params.source?.trim();
|
|
304064
|
+
await packageManager.update(source && source.length > 0 ? source : void 0);
|
|
304065
|
+
pendingReload = true;
|
|
304066
|
+
return {
|
|
304067
|
+
content: [{
|
|
304068
|
+
type: "text",
|
|
304069
|
+
text: output.join("\n") || `Updated ${source && source.length > 0 ? source : "all configured packages"} and scheduled runtime reload.`
|
|
304070
|
+
}],
|
|
304071
|
+
details: {
|
|
304072
|
+
...detailsBase,
|
|
304073
|
+
source: source ?? null
|
|
304074
|
+
}
|
|
304075
|
+
};
|
|
304076
|
+
}
|
|
304077
|
+
}
|
|
304078
|
+
} catch (err) {
|
|
304079
|
+
return {
|
|
304080
|
+
content: [{
|
|
304081
|
+
type: "text",
|
|
304082
|
+
text: `Package manager error: ${err instanceof Error ? err.message : String(err)}`
|
|
304083
|
+
}],
|
|
304084
|
+
details: detailsBase
|
|
304085
|
+
};
|
|
304086
|
+
}
|
|
304087
|
+
}
|
|
304088
|
+
});
|
|
304089
|
+
};
|
|
304090
|
+
}
|
|
304091
|
+
//#endregion
|
|
303946
304092
|
//#region src/extensions/reload-runtime.ts
|
|
303947
304093
|
/**
|
|
303948
304094
|
* Create the reload runtime extension factory.
|
|
@@ -304327,6 +304473,7 @@ function buildExtensionFactories(config, cwd) {
|
|
|
304327
304473
|
const extensionFactories = [];
|
|
304328
304474
|
extensionFactories.push(createCronExtension());
|
|
304329
304475
|
extensionFactories.push(createHeartbeatExtension());
|
|
304476
|
+
extensionFactories.push(createPackageManagerExtension(reloadAllSessions));
|
|
304330
304477
|
extensionFactories.push(createReloadRuntimeExtension(reloadAllSessions));
|
|
304331
304478
|
if (config.agent?.restrictToWorkspace ?? true) {
|
|
304332
304479
|
const configuredAllowedPaths = config.agent?.allowedPaths ?? [];
|
|
@@ -308691,7 +308838,7 @@ function execSafe(cmd) {
|
|
|
308691
308838
|
try {
|
|
308692
308839
|
return {
|
|
308693
308840
|
ok: true,
|
|
308694
|
-
stdout: execSync
|
|
308841
|
+
stdout: execSync(cmd, {
|
|
308695
308842
|
encoding: "utf-8",
|
|
308696
308843
|
stdio: [
|
|
308697
308844
|
"pipe",
|
|
@@ -308880,6 +309027,51 @@ async function uninstallService() {
|
|
|
308880
309027
|
process.exit(1);
|
|
308881
309028
|
}
|
|
308882
309029
|
}
|
|
309030
|
+
function hasInstalledService() {
|
|
309031
|
+
if (isMac) return existsSync(launchdPlistPath());
|
|
309032
|
+
if (isLinux) return existsSync(systemdUnitPath());
|
|
309033
|
+
return false;
|
|
309034
|
+
}
|
|
309035
|
+
function restartInstalledService() {
|
|
309036
|
+
if (isMac) {
|
|
309037
|
+
const plistPath = launchdPlistPath();
|
|
309038
|
+
if (!existsSync(plistPath)) {
|
|
309039
|
+
console.error("Clankie is not installed as a launchd agent.");
|
|
309040
|
+
process.exit(1);
|
|
309041
|
+
}
|
|
309042
|
+
const uid = typeof process.getuid === "function" ? process.getuid() : void 0;
|
|
309043
|
+
if (uid === void 0) {
|
|
309044
|
+
console.error("Could not determine current user ID for launchd restart.");
|
|
309045
|
+
process.exit(1);
|
|
309046
|
+
}
|
|
309047
|
+
if (!execSafe(`launchctl bootout gui/${uid} "${plistPath}"`).ok) execSafe(`launchctl unload "${plistPath}"`);
|
|
309048
|
+
const bootstrap = execSafe(`launchctl bootstrap gui/${uid} "${plistPath}"`);
|
|
309049
|
+
if (!bootstrap.ok) {
|
|
309050
|
+
const load = execSafe(`launchctl load "${plistPath}"`);
|
|
309051
|
+
if (!load.ok) {
|
|
309052
|
+
console.error(`launchctl restart failed: ${bootstrap.stderr || load.stderr}`);
|
|
309053
|
+
process.exit(1);
|
|
309054
|
+
}
|
|
309055
|
+
}
|
|
309056
|
+
console.log(`Restarted launchd agent: ${LAUNCHD_LABEL}`);
|
|
309057
|
+
return;
|
|
309058
|
+
}
|
|
309059
|
+
if (isLinux) {
|
|
309060
|
+
if (!existsSync(systemdUnitPath())) {
|
|
309061
|
+
console.error("Clankie is not installed as a systemd user service.");
|
|
309062
|
+
process.exit(1);
|
|
309063
|
+
}
|
|
309064
|
+
const restart = execSafe(`systemctl --user restart ${SERVICE_NAME}.service`);
|
|
309065
|
+
if (!restart.ok) {
|
|
309066
|
+
console.error(`systemd restart failed: ${restart.stderr || restart.stdout}`);
|
|
309067
|
+
process.exit(1);
|
|
309068
|
+
}
|
|
309069
|
+
console.log(`Restarted systemd service: ${SERVICE_NAME}.service`);
|
|
309070
|
+
return;
|
|
309071
|
+
}
|
|
309072
|
+
console.error(`Service management not supported on ${platform$1()}.`);
|
|
309073
|
+
process.exit(1);
|
|
309074
|
+
}
|
|
308883
309075
|
function showServiceLogs() {
|
|
308884
309076
|
if (isMac) logsLaunchd();
|
|
308885
309077
|
else if (isLinux) logsSystemd();
|
|
@@ -308908,6 +309100,8 @@ function showServiceStatus() {
|
|
|
308908
309100
|
* clankie start Start the daemon (channels + agent)
|
|
308909
309101
|
* clankie stop Stop the daemon
|
|
308910
309102
|
* clankie restart Restart the daemon
|
|
309103
|
+
* clankie update Update clankie via npm and restart daemon/service if needed
|
|
309104
|
+
* clankie self-update Alias for update
|
|
308911
309105
|
* clankie status Check daemon status
|
|
308912
309106
|
* clankie daemon install Install as a system service (systemd/launchd)
|
|
308913
309107
|
* clankie daemon uninstall Remove the system service
|
|
@@ -308929,6 +309123,8 @@ Usage:
|
|
|
308929
309123
|
clankie start [--foreground] Start the daemon (foreground by default)
|
|
308930
309124
|
clankie stop Stop the daemon
|
|
308931
309125
|
clankie restart Restart the daemon
|
|
309126
|
+
clankie update Update clankie via npm and restart daemon/service if needed
|
|
309127
|
+
clankie self-update Alias for update
|
|
308932
309128
|
clankie status Check if daemon is running
|
|
308933
309129
|
clankie daemon install Install as a system service (systemd/launchd)
|
|
308934
309130
|
clankie daemon uninstall Remove the system service
|
|
@@ -308978,15 +309174,40 @@ Examples:
|
|
|
308978
309174
|
Credentials are stored at ~/.clankie/auth.json (separate from pi's auth).
|
|
308979
309175
|
`);
|
|
308980
309176
|
}
|
|
308981
|
-
function
|
|
309177
|
+
function getCurrentVersion() {
|
|
308982
309178
|
const packagePath = join(import.meta.dirname, "..", "package.json");
|
|
308983
309179
|
try {
|
|
308984
309180
|
const pkg = JSON.parse(readFileSync(packagePath, "utf-8"));
|
|
308985
|
-
|
|
309181
|
+
return typeof pkg.version === "string" ? pkg.version : void 0;
|
|
308986
309182
|
} catch {
|
|
308987
|
-
|
|
309183
|
+
return;
|
|
308988
309184
|
}
|
|
308989
309185
|
}
|
|
309186
|
+
function printVersion() {
|
|
309187
|
+
const version = getCurrentVersion();
|
|
309188
|
+
if (version) console.log(`clankie ${version}`);
|
|
309189
|
+
else console.log("clankie (version unknown)");
|
|
309190
|
+
}
|
|
309191
|
+
function getGlobalNpmRoot() {
|
|
309192
|
+
const result = spawnSync("npm", ["root", "-g"], { encoding: "utf-8" });
|
|
309193
|
+
if (result.status !== 0) return void 0;
|
|
309194
|
+
return result.stdout.trim() || void 0;
|
|
309195
|
+
}
|
|
309196
|
+
function isInstalledFromGlobalNpm() {
|
|
309197
|
+
const globalRoot = getGlobalNpmRoot();
|
|
309198
|
+
if (!globalRoot) return false;
|
|
309199
|
+
const packageRoot = join(import.meta.dirname, "..");
|
|
309200
|
+
return packageRoot === join(globalRoot, "clankie") || packageRoot.startsWith(`${join(globalRoot, "clankie")}/`);
|
|
309201
|
+
}
|
|
309202
|
+
function getLatestPublishedVersion() {
|
|
309203
|
+
const result = spawnSync("npm", [
|
|
309204
|
+
"view",
|
|
309205
|
+
"clankie",
|
|
309206
|
+
"version"
|
|
309207
|
+
], { encoding: "utf-8" });
|
|
309208
|
+
if (result.status !== 0) return void 0;
|
|
309209
|
+
return result.stdout.trim() || void 0;
|
|
309210
|
+
}
|
|
308990
309211
|
async function cmdSend(args) {
|
|
308991
309212
|
const message = args.join(" ").trim();
|
|
308992
309213
|
if (!message) {
|
|
@@ -309148,6 +309369,47 @@ function cmdStop() {
|
|
|
309148
309369
|
async function cmdRestart() {
|
|
309149
309370
|
await restartDaemon();
|
|
309150
309371
|
}
|
|
309372
|
+
async function cmdUpdate() {
|
|
309373
|
+
const packageName = "clankie";
|
|
309374
|
+
const currentVersion = getCurrentVersion();
|
|
309375
|
+
const latestVersion = getLatestPublishedVersion();
|
|
309376
|
+
const serviceInstalled = hasInstalledService();
|
|
309377
|
+
const daemonRunning = isRunning().running;
|
|
309378
|
+
if (!isInstalledFromGlobalNpm()) {
|
|
309379
|
+
console.error("Self-update only supports npm global installs.");
|
|
309380
|
+
console.error("Please update clankie with the same package manager or workflow you used to install it.");
|
|
309381
|
+
process.exit(1);
|
|
309382
|
+
}
|
|
309383
|
+
if (currentVersion && latestVersion) {
|
|
309384
|
+
console.log(`Current version: ${currentVersion}`);
|
|
309385
|
+
console.log(`Latest version: ${latestVersion}`);
|
|
309386
|
+
if (currentVersion === latestVersion) {
|
|
309387
|
+
console.log("clankie is already up to date.");
|
|
309388
|
+
return;
|
|
309389
|
+
}
|
|
309390
|
+
console.log();
|
|
309391
|
+
} else console.log("Checking latest version via npm failed or returned no version; proceeding with update.");
|
|
309392
|
+
console.log(`Updating ${packageName} via npm...`);
|
|
309393
|
+
const update = spawnSync("npm", [
|
|
309394
|
+
"install",
|
|
309395
|
+
"-g",
|
|
309396
|
+
`${packageName}@latest`
|
|
309397
|
+
], { stdio: "inherit" });
|
|
309398
|
+
if (update.status !== 0) process.exit(update.status ?? 1);
|
|
309399
|
+
console.log("\n✓ clankie updated.");
|
|
309400
|
+
if (serviceInstalled) {
|
|
309401
|
+
console.log("Restarting installed service...");
|
|
309402
|
+
restartInstalledService();
|
|
309403
|
+
return;
|
|
309404
|
+
}
|
|
309405
|
+
if (daemonRunning) {
|
|
309406
|
+
console.log("Restarting daemon with the updated CLI...");
|
|
309407
|
+
const restart = spawnSync("clankie", ["restart"], { stdio: "inherit" });
|
|
309408
|
+
if (restart.status !== 0) process.exit(restart.status ?? 1);
|
|
309409
|
+
return;
|
|
309410
|
+
}
|
|
309411
|
+
console.log("Daemon is not running. Start it with 'clankie start' when ready.");
|
|
309412
|
+
}
|
|
309151
309413
|
function cmdStatus() {
|
|
309152
309414
|
const status = isRunning();
|
|
309153
309415
|
if (status.running) console.log(`Daemon is running (pid ${status.pid}).`);
|
|
@@ -309271,6 +309533,10 @@ async function main() {
|
|
|
309271
309533
|
case "restart":
|
|
309272
309534
|
await cmdRestart();
|
|
309273
309535
|
break;
|
|
309536
|
+
case "update":
|
|
309537
|
+
case "self-update":
|
|
309538
|
+
await cmdUpdate();
|
|
309539
|
+
break;
|
|
309274
309540
|
case "status":
|
|
309275
309541
|
cmdStatus();
|
|
309276
309542
|
break;
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "clankie",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.10.0",
|
|
4
4
|
"description": "A minimal personal AI assistant built on pi's SDK",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"bin": {
|
|
@@ -9,6 +9,7 @@
|
|
|
9
9
|
"files": [
|
|
10
10
|
"dist/",
|
|
11
11
|
"web-ui-dist/",
|
|
12
|
+
"skills/",
|
|
12
13
|
"package.json",
|
|
13
14
|
"README.md"
|
|
14
15
|
],
|
|
@@ -21,7 +22,7 @@
|
|
|
21
22
|
"check:fix": "biome check --fix .",
|
|
22
23
|
"format": "biome format --write .",
|
|
23
24
|
"typecheck": "tsgo --noEmit",
|
|
24
|
-
"typecheck:all": "tsgo --noEmit && cd web-ui && npx tsgo --noEmit && cd ../extensions/clankie-memory && npx tsgo --noEmit"
|
|
25
|
+
"typecheck:all": "tsgo --noEmit && cd web-ui && npx tsgo --noEmit && cd ../extensions/clankie-memory && npx tsgo --noEmit && cd ../clankie-json-ui-render && npx tsgo --noEmit"
|
|
25
26
|
},
|
|
26
27
|
"engines": {
|
|
27
28
|
"node": ">=18.0.0"
|