@oked/claude-code 0.1.4 → 0.1.6
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/dist/cli.js +85 -7
- package/package.json +2 -2
package/dist/cli.js
CHANGED
|
@@ -4,6 +4,7 @@ import { hostname } from "os";
|
|
|
4
4
|
import { join, dirname } from "path";
|
|
5
5
|
import { fileURLToPath } from "url";
|
|
6
6
|
import { spawn } from "child_process";
|
|
7
|
+
import { createInterface } from "readline";
|
|
7
8
|
import { OKedClient, loadOKedConfig, OKED_CONFIG_PATH } from "@oked/sdk";
|
|
8
9
|
const MCP_TOOL_MATCHER = "mcp__.*";
|
|
9
10
|
const DEFAULT_TOOL_MATCHER = `Bash|Write|Edit|Agent|${MCP_TOOL_MATCHER}`;
|
|
@@ -94,12 +95,66 @@ function openBrowser(url) {
|
|
|
94
95
|
args = [url];
|
|
95
96
|
}
|
|
96
97
|
try {
|
|
97
|
-
spawn(cmd, args, { detached: true, stdio: "ignore" })
|
|
98
|
+
const child = spawn(cmd, args, { detached: true, stdio: "ignore" });
|
|
99
|
+
// The launcher may fail asynchronously (e.g. xdg-open missing). Swallow it
|
|
100
|
+
// so it can't crash the process; the URL is always printed as a fallback.
|
|
101
|
+
child.on("error", () => { });
|
|
102
|
+
child.unref();
|
|
103
|
+
return true;
|
|
98
104
|
}
|
|
99
105
|
catch {
|
|
100
106
|
// Best effort. The user has the URL on screen anyway.
|
|
107
|
+
return false;
|
|
101
108
|
}
|
|
102
109
|
}
|
|
110
|
+
/**
|
|
111
|
+
* Best-effort copy to the OS clipboard. Resolves true only if a clipboard tool
|
|
112
|
+
* actually accepted the text, so the "(copied to clipboard)" line never lies on
|
|
113
|
+
* a headless box. Linux is tried via wl-copy (Wayland), then xclip, then xsel.
|
|
114
|
+
* The code is always printed, so a failure here is purely cosmetic.
|
|
115
|
+
*/
|
|
116
|
+
function copyToClipboard(text) {
|
|
117
|
+
const candidates = process.platform === "darwin"
|
|
118
|
+
? [{ cmd: "pbcopy", args: [] }]
|
|
119
|
+
: process.platform === "win32"
|
|
120
|
+
? [{ cmd: "clip", args: [] }]
|
|
121
|
+
: [
|
|
122
|
+
{ cmd: "wl-copy", args: [] },
|
|
123
|
+
{ cmd: "xclip", args: ["-selection", "clipboard"] },
|
|
124
|
+
{ cmd: "xsel", args: ["--clipboard", "--input"] },
|
|
125
|
+
];
|
|
126
|
+
const tryCopy = ({ cmd, args }) => new Promise((resolve) => {
|
|
127
|
+
let settled = false;
|
|
128
|
+
const done = (ok) => {
|
|
129
|
+
if (!settled) {
|
|
130
|
+
settled = true;
|
|
131
|
+
resolve(ok);
|
|
132
|
+
}
|
|
133
|
+
};
|
|
134
|
+
try {
|
|
135
|
+
const child = spawn(cmd, args, { stdio: ["pipe", "ignore", "ignore"] });
|
|
136
|
+
child.on("error", () => done(false)); // tool missing / not spawnable
|
|
137
|
+
child.on("close", (code) => done(code === 0));
|
|
138
|
+
child.stdin?.on("error", () => { }); // ignore EPIPE if the tool died early
|
|
139
|
+
child.stdin?.end(text);
|
|
140
|
+
}
|
|
141
|
+
catch {
|
|
142
|
+
done(false);
|
|
143
|
+
}
|
|
144
|
+
});
|
|
145
|
+
// Try each candidate in order; stop at the first that succeeds.
|
|
146
|
+
return candidates.reduce((acc, cand) => acc.then((ok) => (ok ? true : tryCopy(cand))), Promise.resolve(false));
|
|
147
|
+
}
|
|
148
|
+
/** Wait for the user to press Enter. Resolves immediately if stdin is closed. */
|
|
149
|
+
function waitForEnter(message) {
|
|
150
|
+
return new Promise((resolve) => {
|
|
151
|
+
const rl = createInterface({ input: process.stdin, output: process.stdout });
|
|
152
|
+
rl.question(message, () => {
|
|
153
|
+
rl.close();
|
|
154
|
+
resolve();
|
|
155
|
+
});
|
|
156
|
+
});
|
|
157
|
+
}
|
|
103
158
|
async function pair(clientType) {
|
|
104
159
|
const codeRes = await fetch(`${DEFAULT_BACKEND_URL}/api/v1/device/code`, {
|
|
105
160
|
method: "POST",
|
|
@@ -116,14 +171,36 @@ async function pair(clientType) {
|
|
|
116
171
|
}
|
|
117
172
|
const code = (await codeRes.json());
|
|
118
173
|
console.log("");
|
|
119
|
-
console.log("
|
|
120
|
-
console.log(` ${code.verification_uri_complete}`);
|
|
174
|
+
console.log(" Pair this device with your OKed account.");
|
|
121
175
|
console.log("");
|
|
122
|
-
console.log("
|
|
176
|
+
console.log(" Your pairing code:");
|
|
123
177
|
console.log(` ${code.user_code}`);
|
|
178
|
+
if (await copyToClipboard(code.user_code)) {
|
|
179
|
+
console.log(" (copied to clipboard)");
|
|
180
|
+
}
|
|
181
|
+
console.log("");
|
|
182
|
+
// Let the user read the code and initiate the browser launch themselves, so
|
|
183
|
+
// the popup isn't a surprise. Skip the prompt when non-interactive (no TTY,
|
|
184
|
+
// or `--yes`/`-y`) so CI and scripted installs still work.
|
|
185
|
+
const interactive = Boolean(process.stdin.isTTY) &&
|
|
186
|
+
!process.argv.includes("--yes") &&
|
|
187
|
+
!process.argv.includes("-y");
|
|
188
|
+
if (interactive) {
|
|
189
|
+
await waitForEnter(" Press Enter to open your browser (Ctrl+C to cancel)... ");
|
|
190
|
+
}
|
|
191
|
+
const opened = openBrowser(code.verification_uri_complete);
|
|
192
|
+
console.log("");
|
|
193
|
+
if (opened) {
|
|
194
|
+
console.log(" Opened your browser to:");
|
|
195
|
+
}
|
|
196
|
+
else {
|
|
197
|
+
console.log(" Couldn't open your browser automatically. Open this link:");
|
|
198
|
+
}
|
|
199
|
+
console.log(` ${code.verification_uri_complete}`);
|
|
200
|
+
console.log("");
|
|
201
|
+
console.log(` (Or visit ${code.verification_uri} and enter the code above.)`);
|
|
124
202
|
console.log("");
|
|
125
|
-
console.log(" Waiting for confirmation
|
|
126
|
-
openBrowser(code.verification_uri_complete);
|
|
203
|
+
console.log(" Waiting for confirmation...");
|
|
127
204
|
const deadline = Date.now() + code.expires_in * 1000;
|
|
128
205
|
const intervalMs = Math.max(1, code.interval) * 1000;
|
|
129
206
|
while (Date.now() < deadline) {
|
|
@@ -202,9 +279,10 @@ async function init() {
|
|
|
202
279
|
return;
|
|
203
280
|
writeOkedConfig(apiKey, DEFAULT_BACKEND_URL);
|
|
204
281
|
console.log("");
|
|
205
|
-
console.log(` Paired
|
|
282
|
+
console.log(` Paired ✓ Key saved to ${OKED_CONFIG_PATH}`);
|
|
206
283
|
console.log("");
|
|
207
284
|
console.log("Every Claude Code session in this project is now protected.");
|
|
285
|
+
console.log("Open a new Claude Code session to activate the hook.");
|
|
208
286
|
}
|
|
209
287
|
async function status() {
|
|
210
288
|
const settingsPath = getSettingsPath();
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@oked/claude-code",
|
|
3
|
-
"version": "0.1.
|
|
3
|
+
"version": "0.1.6",
|
|
4
4
|
"description": "OKed for Claude Code - zero-code human approval for sensitive actions",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"bin": {
|
|
@@ -43,7 +43,7 @@
|
|
|
43
43
|
"access": "public"
|
|
44
44
|
},
|
|
45
45
|
"dependencies": {
|
|
46
|
-
"@oked/sdk": "0.1.
|
|
46
|
+
"@oked/sdk": "0.1.6"
|
|
47
47
|
},
|
|
48
48
|
"devDependencies": {
|
|
49
49
|
"typescript": "^5.6.0"
|