@melihmucuk/leash 1.0.3 → 1.0.4
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 +35 -14
- package/bin/leash.js +30 -0
- package/bin/lib.js +92 -26
- package/dist/claude-code/leash.js +56 -3
- package/dist/factory/leash.js +56 -3
- package/dist/opencode/leash.js +52 -0
- package/dist/pi/leash.js +49 -0
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -19,26 +19,27 @@ AI agents can hallucinate dangerous commands. Leash sandboxes them:
|
|
|
19
19
|
## Quick Start
|
|
20
20
|
|
|
21
21
|
```bash
|
|
22
|
+
# Install leash globally
|
|
22
23
|
npm install -g @melihmucuk/leash
|
|
24
|
+
|
|
25
|
+
# Setup leash for your platform
|
|
23
26
|
leash --setup <platform>
|
|
24
|
-
```
|
|
25
27
|
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
| OpenCode | `leash --setup opencode` |
|
|
29
|
-
| Pi Coding Agent | `leash --setup pi` |
|
|
30
|
-
| Claude Code | `leash --setup claude-code` |
|
|
31
|
-
| Factory Droid | `leash --setup factory` |
|
|
28
|
+
# Remove leash from a platform
|
|
29
|
+
leash --remove <platform>
|
|
32
30
|
|
|
33
|
-
|
|
31
|
+
# Update leash anytime
|
|
32
|
+
leash --update
|
|
33
|
+
```
|
|
34
34
|
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
35
|
+
| Platform | Command |
|
|
36
|
+
| --------------- | --------------------------- |
|
|
37
|
+
| OpenCode | `leash --setup opencode` |
|
|
38
|
+
| Pi Coding Agent | `leash --setup pi` |
|
|
39
|
+
| Claude Code | `leash --setup claude-code` |
|
|
40
|
+
| Factory Droid | `leash --setup factory` |
|
|
38
41
|
|
|
39
|
-
|
|
40
|
-
leash --remove <platform>
|
|
41
|
-
```
|
|
42
|
+
Restart your agent. Done!
|
|
42
43
|
|
|
43
44
|
<details>
|
|
44
45
|
<summary><b>Manual Setup</b></summary>
|
|
@@ -72,6 +73,16 @@ Add to `~/.claude/settings.json`:
|
|
|
72
73
|
```json
|
|
73
74
|
{
|
|
74
75
|
"hooks": {
|
|
76
|
+
"SessionStart": [
|
|
77
|
+
{
|
|
78
|
+
"hooks": [
|
|
79
|
+
{
|
|
80
|
+
"type": "command",
|
|
81
|
+
"command": "node <path from leash --path claude-code>"
|
|
82
|
+
}
|
|
83
|
+
]
|
|
84
|
+
}
|
|
85
|
+
],
|
|
75
86
|
"PreToolUse": [
|
|
76
87
|
{
|
|
77
88
|
"matcher": "Bash|Write|Edit",
|
|
@@ -94,6 +105,16 @@ Add to `~/.factory/settings.json`:
|
|
|
94
105
|
```json
|
|
95
106
|
{
|
|
96
107
|
"hooks": {
|
|
108
|
+
"SessionStart": [
|
|
109
|
+
{
|
|
110
|
+
"hooks": [
|
|
111
|
+
{
|
|
112
|
+
"type": "command",
|
|
113
|
+
"command": "node <path from leash --path factory>"
|
|
114
|
+
}
|
|
115
|
+
]
|
|
116
|
+
}
|
|
117
|
+
],
|
|
97
118
|
"PreToolUse": [
|
|
98
119
|
{
|
|
99
120
|
"matcher": "Execute|Write|Edit",
|
package/bin/leash.js
CHANGED
|
@@ -4,7 +4,9 @@ import { existsSync } from "fs";
|
|
|
4
4
|
import { dirname, join } from "path";
|
|
5
5
|
import { homedir } from "os";
|
|
6
6
|
import { fileURLToPath } from "url";
|
|
7
|
+
import { execSync } from "child_process";
|
|
7
8
|
import { PLATFORMS, setupPlatform, removePlatform } from "./lib.js";
|
|
9
|
+
import { checkForUpdates } from "../packages/core/lib/version-checker.js";
|
|
8
10
|
|
|
9
11
|
const __dirname = dirname(fileURLToPath(import.meta.url));
|
|
10
12
|
|
|
@@ -107,6 +109,28 @@ function showPath(platformKey) {
|
|
|
107
109
|
console.log(leashPath);
|
|
108
110
|
}
|
|
109
111
|
|
|
112
|
+
async function update() {
|
|
113
|
+
console.log("Checking for updates...");
|
|
114
|
+
|
|
115
|
+
const result = await checkForUpdates();
|
|
116
|
+
|
|
117
|
+
if (!result.hasUpdate) {
|
|
118
|
+
console.log(`[ok] Already up to date (v${result.currentVersion})`);
|
|
119
|
+
return;
|
|
120
|
+
}
|
|
121
|
+
|
|
122
|
+
console.log(`[ok] Update available: v${result.currentVersion} → v${result.latestVersion}`);
|
|
123
|
+
console.log("[ok] Updating...");
|
|
124
|
+
|
|
125
|
+
try {
|
|
126
|
+
execSync("npm update -g @melihmucuk/leash", { stdio: "inherit" });
|
|
127
|
+
console.log("[ok] Update complete");
|
|
128
|
+
} catch {
|
|
129
|
+
console.error("[error] Update failed. Try manually: npm update -g @melihmucuk/leash");
|
|
130
|
+
process.exit(1);
|
|
131
|
+
}
|
|
132
|
+
}
|
|
133
|
+
|
|
110
134
|
function showHelp() {
|
|
111
135
|
console.log(`
|
|
112
136
|
leash - Security guardrails for AI coding agents
|
|
@@ -115,6 +139,7 @@ Usage:
|
|
|
115
139
|
leash --setup <platform> Install leash for a platform
|
|
116
140
|
leash --remove <platform> Remove leash from a platform
|
|
117
141
|
leash --path <platform> Show leash path for a platform
|
|
142
|
+
leash --update Update leash to latest version
|
|
118
143
|
leash --help Show this help
|
|
119
144
|
|
|
120
145
|
Platforms:
|
|
@@ -127,6 +152,7 @@ Examples:
|
|
|
127
152
|
leash --setup opencode
|
|
128
153
|
leash --remove claude-code
|
|
129
154
|
leash --path pi
|
|
155
|
+
leash --update
|
|
130
156
|
`);
|
|
131
157
|
}
|
|
132
158
|
|
|
@@ -162,6 +188,10 @@ switch (command) {
|
|
|
162
188
|
}
|
|
163
189
|
showPath(platform);
|
|
164
190
|
break;
|
|
191
|
+
case "--update":
|
|
192
|
+
case "-u":
|
|
193
|
+
await update();
|
|
194
|
+
break;
|
|
165
195
|
case "--help":
|
|
166
196
|
case "-h":
|
|
167
197
|
case undefined:
|
package/bin/lib.js
CHANGED
|
@@ -49,26 +49,59 @@ export const PLATFORMS = {
|
|
|
49
49
|
distPath: "claude-code/leash.js",
|
|
50
50
|
setup: (config, leashPath) => {
|
|
51
51
|
config.hooks = config.hooks || {};
|
|
52
|
-
|
|
53
|
-
|
|
52
|
+
const hookCommand = { type: "command", command: `node ${leashPath}` };
|
|
53
|
+
|
|
54
|
+
// Check if already installed in either hook
|
|
55
|
+
const inSessionStart = config.hooks.SessionStart?.some((entry) =>
|
|
56
|
+
entry.hooks?.some((h) => h.command?.includes("leash"))
|
|
57
|
+
);
|
|
58
|
+
const inPreToolUse = config.hooks.PreToolUse?.some((entry) =>
|
|
54
59
|
entry.hooks?.some((h) => h.command?.includes("leash"))
|
|
55
60
|
);
|
|
56
|
-
if (
|
|
61
|
+
if (inSessionStart && inPreToolUse) {
|
|
57
62
|
return { skipped: true };
|
|
58
63
|
}
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
64
|
+
|
|
65
|
+
// Add SessionStart hook
|
|
66
|
+
if (!inSessionStart) {
|
|
67
|
+
config.hooks.SessionStart = config.hooks.SessionStart || [];
|
|
68
|
+
config.hooks.SessionStart.push({
|
|
69
|
+
hooks: [hookCommand],
|
|
70
|
+
});
|
|
71
|
+
}
|
|
72
|
+
|
|
73
|
+
// Add PreToolUse hook
|
|
74
|
+
if (!inPreToolUse) {
|
|
75
|
+
config.hooks.PreToolUse = config.hooks.PreToolUse || [];
|
|
76
|
+
config.hooks.PreToolUse.push({
|
|
77
|
+
matcher: "Bash|Write|Edit",
|
|
78
|
+
hooks: [hookCommand],
|
|
79
|
+
});
|
|
80
|
+
}
|
|
81
|
+
|
|
63
82
|
return { skipped: false };
|
|
64
83
|
},
|
|
65
84
|
remove: (config) => {
|
|
66
|
-
if (!config.hooks
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
85
|
+
if (!config.hooks) return false;
|
|
86
|
+
let removed = false;
|
|
87
|
+
|
|
88
|
+
if (config.hooks.SessionStart) {
|
|
89
|
+
const before = config.hooks.SessionStart.length;
|
|
90
|
+
config.hooks.SessionStart = config.hooks.SessionStart.filter(
|
|
91
|
+
(entry) => !entry.hooks?.some((h) => h.command?.includes("leash"))
|
|
92
|
+
);
|
|
93
|
+
if (config.hooks.SessionStart.length < before) removed = true;
|
|
94
|
+
}
|
|
95
|
+
|
|
96
|
+
if (config.hooks.PreToolUse) {
|
|
97
|
+
const before = config.hooks.PreToolUse.length;
|
|
98
|
+
config.hooks.PreToolUse = config.hooks.PreToolUse.filter(
|
|
99
|
+
(entry) => !entry.hooks?.some((h) => h.command?.includes("leash"))
|
|
100
|
+
);
|
|
101
|
+
if (config.hooks.PreToolUse.length < before) removed = true;
|
|
102
|
+
}
|
|
103
|
+
|
|
104
|
+
return removed;
|
|
72
105
|
},
|
|
73
106
|
},
|
|
74
107
|
factory: {
|
|
@@ -77,26 +110,59 @@ export const PLATFORMS = {
|
|
|
77
110
|
distPath: "factory/leash.js",
|
|
78
111
|
setup: (config, leashPath) => {
|
|
79
112
|
config.hooks = config.hooks || {};
|
|
80
|
-
|
|
81
|
-
|
|
113
|
+
const hookCommand = { type: "command", command: `node ${leashPath}` };
|
|
114
|
+
|
|
115
|
+
// Check if already installed in either hook
|
|
116
|
+
const inSessionStart = config.hooks.SessionStart?.some((entry) =>
|
|
117
|
+
entry.hooks?.some((h) => h.command?.includes("leash"))
|
|
118
|
+
);
|
|
119
|
+
const inPreToolUse = config.hooks.PreToolUse?.some((entry) =>
|
|
82
120
|
entry.hooks?.some((h) => h.command?.includes("leash"))
|
|
83
121
|
);
|
|
84
|
-
if (
|
|
122
|
+
if (inSessionStart && inPreToolUse) {
|
|
85
123
|
return { skipped: true };
|
|
86
124
|
}
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
125
|
+
|
|
126
|
+
// Add SessionStart hook
|
|
127
|
+
if (!inSessionStart) {
|
|
128
|
+
config.hooks.SessionStart = config.hooks.SessionStart || [];
|
|
129
|
+
config.hooks.SessionStart.push({
|
|
130
|
+
hooks: [hookCommand],
|
|
131
|
+
});
|
|
132
|
+
}
|
|
133
|
+
|
|
134
|
+
// Add PreToolUse hook
|
|
135
|
+
if (!inPreToolUse) {
|
|
136
|
+
config.hooks.PreToolUse = config.hooks.PreToolUse || [];
|
|
137
|
+
config.hooks.PreToolUse.push({
|
|
138
|
+
matcher: "Execute|Write|Edit",
|
|
139
|
+
hooks: [hookCommand],
|
|
140
|
+
});
|
|
141
|
+
}
|
|
142
|
+
|
|
91
143
|
return { skipped: false };
|
|
92
144
|
},
|
|
93
145
|
remove: (config) => {
|
|
94
|
-
if (!config.hooks
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
146
|
+
if (!config.hooks) return false;
|
|
147
|
+
let removed = false;
|
|
148
|
+
|
|
149
|
+
if (config.hooks.SessionStart) {
|
|
150
|
+
const before = config.hooks.SessionStart.length;
|
|
151
|
+
config.hooks.SessionStart = config.hooks.SessionStart.filter(
|
|
152
|
+
(entry) => !entry.hooks?.some((h) => h.command?.includes("leash"))
|
|
153
|
+
);
|
|
154
|
+
if (config.hooks.SessionStart.length < before) removed = true;
|
|
155
|
+
}
|
|
156
|
+
|
|
157
|
+
if (config.hooks.PreToolUse) {
|
|
158
|
+
const before = config.hooks.PreToolUse.length;
|
|
159
|
+
config.hooks.PreToolUse = config.hooks.PreToolUse.filter(
|
|
160
|
+
(entry) => !entry.hooks?.some((h) => h.command?.includes("leash"))
|
|
161
|
+
);
|
|
162
|
+
if (config.hooks.PreToolUse.length < before) removed = true;
|
|
163
|
+
}
|
|
164
|
+
|
|
165
|
+
return removed;
|
|
100
166
|
},
|
|
101
167
|
},
|
|
102
168
|
};
|
|
@@ -466,6 +466,48 @@ var CommandAnalyzer = class {
|
|
|
466
466
|
}
|
|
467
467
|
};
|
|
468
468
|
|
|
469
|
+
// packages/core/version-checker.ts
|
|
470
|
+
import { readFileSync, existsSync } from "fs";
|
|
471
|
+
import { dirname, join } from "path";
|
|
472
|
+
import { fileURLToPath } from "url";
|
|
473
|
+
function getVersion() {
|
|
474
|
+
const __dirname = dirname(fileURLToPath(import.meta.url));
|
|
475
|
+
const candidates = [
|
|
476
|
+
join(__dirname, "..", "..", "package.json"),
|
|
477
|
+
join(__dirname, "..", "..", "..", "package.json")
|
|
478
|
+
];
|
|
479
|
+
for (const path of candidates) {
|
|
480
|
+
if (existsSync(path)) {
|
|
481
|
+
try {
|
|
482
|
+
const pkg = JSON.parse(readFileSync(path, "utf-8"));
|
|
483
|
+
if (pkg.name === "@melihmucuk/leash") {
|
|
484
|
+
return pkg.version;
|
|
485
|
+
}
|
|
486
|
+
} catch {
|
|
487
|
+
}
|
|
488
|
+
}
|
|
489
|
+
}
|
|
490
|
+
return "0.0.0";
|
|
491
|
+
}
|
|
492
|
+
var CURRENT_VERSION = getVersion();
|
|
493
|
+
var NPM_REGISTRY_URL = "https://registry.npmjs.org/@melihmucuk/leash/latest";
|
|
494
|
+
async function checkForUpdates() {
|
|
495
|
+
try {
|
|
496
|
+
const response = await fetch(NPM_REGISTRY_URL);
|
|
497
|
+
if (!response.ok) {
|
|
498
|
+
return { hasUpdate: false, currentVersion: CURRENT_VERSION };
|
|
499
|
+
}
|
|
500
|
+
const data = await response.json();
|
|
501
|
+
return {
|
|
502
|
+
hasUpdate: data.version !== CURRENT_VERSION,
|
|
503
|
+
latestVersion: data.version,
|
|
504
|
+
currentVersion: CURRENT_VERSION
|
|
505
|
+
};
|
|
506
|
+
} catch {
|
|
507
|
+
return { hasUpdate: false, currentVersion: CURRENT_VERSION };
|
|
508
|
+
}
|
|
509
|
+
}
|
|
510
|
+
|
|
469
511
|
// packages/claude-code/leash.ts
|
|
470
512
|
async function readStdin() {
|
|
471
513
|
const chunks = [];
|
|
@@ -483,10 +525,21 @@ async function main() {
|
|
|
483
525
|
console.error("Failed to parse input JSON");
|
|
484
526
|
process.exit(1);
|
|
485
527
|
}
|
|
486
|
-
const { tool_name, tool_input, cwd } = input;
|
|
528
|
+
const { hook_event_name, tool_name, tool_input, cwd } = input;
|
|
529
|
+
if (hook_event_name === "SessionStart") {
|
|
530
|
+
const messages = ["\u{1F512} Leash active"];
|
|
531
|
+
const update = await checkForUpdates();
|
|
532
|
+
if (update.hasUpdate) {
|
|
533
|
+
messages.push(
|
|
534
|
+
`\u{1F504} Leash ${update.latestVersion} available. Run: leash --update`
|
|
535
|
+
);
|
|
536
|
+
}
|
|
537
|
+
console.log(JSON.stringify({ systemMessage: messages.join("\n") }));
|
|
538
|
+
process.exit(0);
|
|
539
|
+
}
|
|
487
540
|
const analyzer = new CommandAnalyzer(cwd);
|
|
488
541
|
if (tool_name === "Bash") {
|
|
489
|
-
const command = tool_input
|
|
542
|
+
const command = tool_input?.command || "";
|
|
490
543
|
const result = analyzer.analyze(command);
|
|
491
544
|
if (result.blocked) {
|
|
492
545
|
console.error(
|
|
@@ -499,7 +552,7 @@ Action: Guide the user to run the command manually.`
|
|
|
499
552
|
}
|
|
500
553
|
}
|
|
501
554
|
if (tool_name === "Write" || tool_name === "Edit") {
|
|
502
|
-
const path = tool_input
|
|
555
|
+
const path = tool_input?.file_path || "";
|
|
503
556
|
const result = analyzer.validatePath(path);
|
|
504
557
|
if (result.blocked) {
|
|
505
558
|
console.error(
|
package/dist/factory/leash.js
CHANGED
|
@@ -466,6 +466,48 @@ var CommandAnalyzer = class {
|
|
|
466
466
|
}
|
|
467
467
|
};
|
|
468
468
|
|
|
469
|
+
// packages/core/version-checker.ts
|
|
470
|
+
import { readFileSync, existsSync } from "fs";
|
|
471
|
+
import { dirname, join } from "path";
|
|
472
|
+
import { fileURLToPath } from "url";
|
|
473
|
+
function getVersion() {
|
|
474
|
+
const __dirname = dirname(fileURLToPath(import.meta.url));
|
|
475
|
+
const candidates = [
|
|
476
|
+
join(__dirname, "..", "..", "package.json"),
|
|
477
|
+
join(__dirname, "..", "..", "..", "package.json")
|
|
478
|
+
];
|
|
479
|
+
for (const path of candidates) {
|
|
480
|
+
if (existsSync(path)) {
|
|
481
|
+
try {
|
|
482
|
+
const pkg = JSON.parse(readFileSync(path, "utf-8"));
|
|
483
|
+
if (pkg.name === "@melihmucuk/leash") {
|
|
484
|
+
return pkg.version;
|
|
485
|
+
}
|
|
486
|
+
} catch {
|
|
487
|
+
}
|
|
488
|
+
}
|
|
489
|
+
}
|
|
490
|
+
return "0.0.0";
|
|
491
|
+
}
|
|
492
|
+
var CURRENT_VERSION = getVersion();
|
|
493
|
+
var NPM_REGISTRY_URL = "https://registry.npmjs.org/@melihmucuk/leash/latest";
|
|
494
|
+
async function checkForUpdates() {
|
|
495
|
+
try {
|
|
496
|
+
const response = await fetch(NPM_REGISTRY_URL);
|
|
497
|
+
if (!response.ok) {
|
|
498
|
+
return { hasUpdate: false, currentVersion: CURRENT_VERSION };
|
|
499
|
+
}
|
|
500
|
+
const data = await response.json();
|
|
501
|
+
return {
|
|
502
|
+
hasUpdate: data.version !== CURRENT_VERSION,
|
|
503
|
+
latestVersion: data.version,
|
|
504
|
+
currentVersion: CURRENT_VERSION
|
|
505
|
+
};
|
|
506
|
+
} catch {
|
|
507
|
+
return { hasUpdate: false, currentVersion: CURRENT_VERSION };
|
|
508
|
+
}
|
|
509
|
+
}
|
|
510
|
+
|
|
469
511
|
// packages/factory/leash.ts
|
|
470
512
|
async function readStdin() {
|
|
471
513
|
const chunks = [];
|
|
@@ -483,11 +525,22 @@ async function main() {
|
|
|
483
525
|
console.error("Failed to parse input JSON");
|
|
484
526
|
process.exit(1);
|
|
485
527
|
}
|
|
486
|
-
const { tool_name, tool_input } = input;
|
|
528
|
+
const { hook_event_name, tool_name, tool_input } = input;
|
|
487
529
|
const cwd = process.env.FACTORY_PROJECT_DIR || input.cwd || process.cwd();
|
|
530
|
+
if (hook_event_name === "SessionStart") {
|
|
531
|
+
const messages = ["\u{1F512} Leash active"];
|
|
532
|
+
const update = await checkForUpdates();
|
|
533
|
+
if (update.hasUpdate) {
|
|
534
|
+
messages.push(
|
|
535
|
+
`\u{1F504} Leash ${update.latestVersion} available. Run: leash --update`
|
|
536
|
+
);
|
|
537
|
+
}
|
|
538
|
+
console.log(JSON.stringify({ systemMessage: messages.join("\n") }));
|
|
539
|
+
process.exit(0);
|
|
540
|
+
}
|
|
488
541
|
const analyzer = new CommandAnalyzer(cwd);
|
|
489
542
|
if (tool_name === "Execute") {
|
|
490
|
-
const command = tool_input
|
|
543
|
+
const command = tool_input?.command || "";
|
|
491
544
|
const result = analyzer.analyze(command);
|
|
492
545
|
if (result.blocked) {
|
|
493
546
|
console.error(
|
|
@@ -500,7 +553,7 @@ Action: Guide the user to run the command manually.`
|
|
|
500
553
|
}
|
|
501
554
|
}
|
|
502
555
|
if (tool_name === "Write" || tool_name === "Edit") {
|
|
503
|
-
const path = tool_input
|
|
556
|
+
const path = tool_input?.file_path || "";
|
|
504
557
|
const result = analyzer.validatePath(path);
|
|
505
558
|
if (result.blocked) {
|
|
506
559
|
console.error(
|
package/dist/opencode/leash.js
CHANGED
|
@@ -464,6 +464,48 @@ var CommandAnalyzer = class {
|
|
|
464
464
|
}
|
|
465
465
|
};
|
|
466
466
|
|
|
467
|
+
// packages/core/version-checker.ts
|
|
468
|
+
import { readFileSync, existsSync } from "fs";
|
|
469
|
+
import { dirname, join } from "path";
|
|
470
|
+
import { fileURLToPath } from "url";
|
|
471
|
+
function getVersion() {
|
|
472
|
+
const __dirname = dirname(fileURLToPath(import.meta.url));
|
|
473
|
+
const candidates = [
|
|
474
|
+
join(__dirname, "..", "..", "package.json"),
|
|
475
|
+
join(__dirname, "..", "..", "..", "package.json")
|
|
476
|
+
];
|
|
477
|
+
for (const path of candidates) {
|
|
478
|
+
if (existsSync(path)) {
|
|
479
|
+
try {
|
|
480
|
+
const pkg = JSON.parse(readFileSync(path, "utf-8"));
|
|
481
|
+
if (pkg.name === "@melihmucuk/leash") {
|
|
482
|
+
return pkg.version;
|
|
483
|
+
}
|
|
484
|
+
} catch {
|
|
485
|
+
}
|
|
486
|
+
}
|
|
487
|
+
}
|
|
488
|
+
return "0.0.0";
|
|
489
|
+
}
|
|
490
|
+
var CURRENT_VERSION = getVersion();
|
|
491
|
+
var NPM_REGISTRY_URL = "https://registry.npmjs.org/@melihmucuk/leash/latest";
|
|
492
|
+
async function checkForUpdates() {
|
|
493
|
+
try {
|
|
494
|
+
const response = await fetch(NPM_REGISTRY_URL);
|
|
495
|
+
if (!response.ok) {
|
|
496
|
+
return { hasUpdate: false, currentVersion: CURRENT_VERSION };
|
|
497
|
+
}
|
|
498
|
+
const data = await response.json();
|
|
499
|
+
return {
|
|
500
|
+
hasUpdate: data.version !== CURRENT_VERSION,
|
|
501
|
+
latestVersion: data.version,
|
|
502
|
+
currentVersion: CURRENT_VERSION
|
|
503
|
+
};
|
|
504
|
+
} catch {
|
|
505
|
+
return { hasUpdate: false, currentVersion: CURRENT_VERSION };
|
|
506
|
+
}
|
|
507
|
+
}
|
|
508
|
+
|
|
467
509
|
// packages/opencode/leash.ts
|
|
468
510
|
var Leash = async ({ directory, client }) => {
|
|
469
511
|
const analyzer = new CommandAnalyzer(directory);
|
|
@@ -473,6 +515,16 @@ var Leash = async ({ directory, client }) => {
|
|
|
473
515
|
await client.tui.showToast({
|
|
474
516
|
body: { message: "\u{1F512} Leash active", variant: "info" }
|
|
475
517
|
});
|
|
518
|
+
const update = await checkForUpdates();
|
|
519
|
+
if (update.hasUpdate) {
|
|
520
|
+
await client.tui.showToast({
|
|
521
|
+
body: {
|
|
522
|
+
message: `\u{1F504} Leash ${update.latestVersion} available.
|
|
523
|
+
Run: leash --update (restart required)`,
|
|
524
|
+
variant: "warning"
|
|
525
|
+
}
|
|
526
|
+
});
|
|
527
|
+
}
|
|
476
528
|
}
|
|
477
529
|
},
|
|
478
530
|
"tool.execute.before": async (input, output) => {
|
package/dist/pi/leash.js
CHANGED
|
@@ -464,6 +464,48 @@ var CommandAnalyzer = class {
|
|
|
464
464
|
}
|
|
465
465
|
};
|
|
466
466
|
|
|
467
|
+
// packages/core/version-checker.ts
|
|
468
|
+
import { readFileSync, existsSync } from "fs";
|
|
469
|
+
import { dirname, join } from "path";
|
|
470
|
+
import { fileURLToPath } from "url";
|
|
471
|
+
function getVersion() {
|
|
472
|
+
const __dirname = dirname(fileURLToPath(import.meta.url));
|
|
473
|
+
const candidates = [
|
|
474
|
+
join(__dirname, "..", "..", "package.json"),
|
|
475
|
+
join(__dirname, "..", "..", "..", "package.json")
|
|
476
|
+
];
|
|
477
|
+
for (const path of candidates) {
|
|
478
|
+
if (existsSync(path)) {
|
|
479
|
+
try {
|
|
480
|
+
const pkg = JSON.parse(readFileSync(path, "utf-8"));
|
|
481
|
+
if (pkg.name === "@melihmucuk/leash") {
|
|
482
|
+
return pkg.version;
|
|
483
|
+
}
|
|
484
|
+
} catch {
|
|
485
|
+
}
|
|
486
|
+
}
|
|
487
|
+
}
|
|
488
|
+
return "0.0.0";
|
|
489
|
+
}
|
|
490
|
+
var CURRENT_VERSION = getVersion();
|
|
491
|
+
var NPM_REGISTRY_URL = "https://registry.npmjs.org/@melihmucuk/leash/latest";
|
|
492
|
+
async function checkForUpdates() {
|
|
493
|
+
try {
|
|
494
|
+
const response = await fetch(NPM_REGISTRY_URL);
|
|
495
|
+
if (!response.ok) {
|
|
496
|
+
return { hasUpdate: false, currentVersion: CURRENT_VERSION };
|
|
497
|
+
}
|
|
498
|
+
const data = await response.json();
|
|
499
|
+
return {
|
|
500
|
+
hasUpdate: data.version !== CURRENT_VERSION,
|
|
501
|
+
latestVersion: data.version,
|
|
502
|
+
currentVersion: CURRENT_VERSION
|
|
503
|
+
};
|
|
504
|
+
} catch {
|
|
505
|
+
return { hasUpdate: false, currentVersion: CURRENT_VERSION };
|
|
506
|
+
}
|
|
507
|
+
}
|
|
508
|
+
|
|
467
509
|
// packages/pi/leash.ts
|
|
468
510
|
function leash_default(pi) {
|
|
469
511
|
let analyzer = null;
|
|
@@ -471,6 +513,13 @@ function leash_default(pi) {
|
|
|
471
513
|
if (event.reason === "start") {
|
|
472
514
|
analyzer = new CommandAnalyzer(ctx.cwd);
|
|
473
515
|
ctx.ui.notify("\u{1F512} Leash active", "info");
|
|
516
|
+
const update = await checkForUpdates();
|
|
517
|
+
if (update.hasUpdate) {
|
|
518
|
+
ctx.ui.notify(
|
|
519
|
+
`\u{1F504} Leash ${update.latestVersion} available. Run: leash --update (restart required)`,
|
|
520
|
+
"warning"
|
|
521
|
+
);
|
|
522
|
+
}
|
|
474
523
|
}
|
|
475
524
|
});
|
|
476
525
|
pi.on("tool_call", async (event, ctx) => {
|