hostwares-cli 2.4.4 → 2.4.5
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/index.js +63 -25
- package/package.json +1 -1
package/dist/index.js
CHANGED
|
@@ -2031,14 +2031,14 @@ async function executePending(calls, opts, emit, checkpoint) {
|
|
|
2031
2031
|
let oldContent = null;
|
|
2032
2032
|
if (WRITE_TOOLS.has(call.name) && typeof call.input?.path === "string" && call.input.path) {
|
|
2033
2033
|
try {
|
|
2034
|
-
const { readFileSync:
|
|
2035
|
-
const { resolve: pathResolve2, join:
|
|
2036
|
-
const { homedir:
|
|
2034
|
+
const { readFileSync: readFileSync10, existsSync: existsSync9 } = await import("fs");
|
|
2035
|
+
const { resolve: pathResolve2, join: join10 } = await import("path");
|
|
2036
|
+
const { homedir: homedir6 } = await import("os");
|
|
2037
2037
|
const raw = String(call.input.path);
|
|
2038
|
-
const expanded = raw === "~" ?
|
|
2038
|
+
const expanded = raw === "~" ? homedir6() : raw.startsWith("~/") ? join10(homedir6(), raw.slice(2)) : raw;
|
|
2039
2039
|
const abs = pathResolve2(expanded);
|
|
2040
|
-
if (
|
|
2041
|
-
oldContent =
|
|
2040
|
+
if (existsSync9(abs))
|
|
2041
|
+
oldContent = readFileSync10(abs, "utf8");
|
|
2042
2042
|
else
|
|
2043
2043
|
oldContent = "";
|
|
2044
2044
|
} catch {
|
|
@@ -2721,9 +2721,9 @@ var init_device = __esm({
|
|
|
2721
2721
|
});
|
|
2722
2722
|
|
|
2723
2723
|
// src/index.ts
|
|
2724
|
-
import { readFileSync as
|
|
2724
|
+
import { readFileSync as readFileSync9 } from "fs";
|
|
2725
2725
|
import { fileURLToPath } from "url";
|
|
2726
|
-
import { dirname as dirname4, join as
|
|
2726
|
+
import { dirname as dirname4, join as join9 } from "path";
|
|
2727
2727
|
|
|
2728
2728
|
// src/agent/run.ts
|
|
2729
2729
|
init_esm();
|
|
@@ -2883,6 +2883,9 @@ async function pickScope(options) {
|
|
|
2883
2883
|
init_theme();
|
|
2884
2884
|
init_render();
|
|
2885
2885
|
init_esm();
|
|
2886
|
+
import { existsSync as existsSync7, readFileSync as readFileSync7, writeFileSync as writeFileSync5, mkdirSync as mkdirSync5, renameSync as renameSync2, chmodSync as chmodSync2 } from "fs";
|
|
2887
|
+
import { join as join7 } from "path";
|
|
2888
|
+
import { homedir as homedir5 } from "os";
|
|
2886
2889
|
var READ_ONLY = /* @__PURE__ */ new Set([
|
|
2887
2890
|
"read_file",
|
|
2888
2891
|
"list_directory",
|
|
@@ -2923,6 +2926,40 @@ function permissionRules() {
|
|
|
2923
2926
|
}
|
|
2924
2927
|
return cachedRules;
|
|
2925
2928
|
}
|
|
2929
|
+
function reloadPermissionRules() {
|
|
2930
|
+
cachedRules = null;
|
|
2931
|
+
}
|
|
2932
|
+
function saveGlobalPermissionRule(rule) {
|
|
2933
|
+
const { HW_DIR: HW_DIR2 } = getHwDir();
|
|
2934
|
+
const file = join7(HW_DIR2, "permissions.json");
|
|
2935
|
+
let rules = [];
|
|
2936
|
+
try {
|
|
2937
|
+
if (existsSync7(file)) {
|
|
2938
|
+
const parsed = JSON.parse(readFileSync7(file, "utf8"));
|
|
2939
|
+
const arr = Array.isArray(parsed) ? parsed : Array.isArray(parsed?.rules) ? parsed.rules : [];
|
|
2940
|
+
rules = arr.filter((r) => !!r && typeof r.tool === "string" && ["allow", "ask", "deny"].includes(r.effect));
|
|
2941
|
+
}
|
|
2942
|
+
} catch {
|
|
2943
|
+
rules = [];
|
|
2944
|
+
}
|
|
2945
|
+
const exists = rules.some((r) => r.tool === rule.tool && (r.match ?? "") === (rule.match ?? "") && r.effect === rule.effect);
|
|
2946
|
+
if (!exists) rules.push(rule);
|
|
2947
|
+
try {
|
|
2948
|
+
if (!existsSync7(HW_DIR2)) mkdirSync5(HW_DIR2, { recursive: true, mode: 448 });
|
|
2949
|
+
const tmp = join7(HW_DIR2, `permissions.json.tmp-${process.pid}`);
|
|
2950
|
+
writeFileSync5(tmp, JSON.stringify(rules, null, 2) + "\n", { mode: 384 });
|
|
2951
|
+
renameSync2(tmp, file);
|
|
2952
|
+
try {
|
|
2953
|
+
chmodSync2(file, 384);
|
|
2954
|
+
} catch {
|
|
2955
|
+
}
|
|
2956
|
+
} catch {
|
|
2957
|
+
}
|
|
2958
|
+
reloadPermissionRules();
|
|
2959
|
+
}
|
|
2960
|
+
function getHwDir() {
|
|
2961
|
+
return { HW_DIR: join7(homedir5(), ".hostwares") };
|
|
2962
|
+
}
|
|
2926
2963
|
function isProjectTrusted(cwd = process.cwd()) {
|
|
2927
2964
|
return (getConfig().trustedProjects ?? []).includes(cwd);
|
|
2928
2965
|
}
|
|
@@ -2967,15 +3004,11 @@ ${c.yellow(glyph.caret)} ${describe(tool)}
|
|
|
2967
3004
|
const toolPath = String(tool.input.path ?? tool.input.localPath ?? "");
|
|
2968
3005
|
const rel = toolPath ? shortRel(toolPath) : "";
|
|
2969
3006
|
const dir = rel ? rel.split("/").slice(0, -1).join("/") || "." : "";
|
|
2970
|
-
const scopeOpts = [
|
|
2971
|
-
`Specific paths \u2192 ${rel || tool.name} ${c.dim("(this file only)")}`,
|
|
2972
|
-
`Complete directory \u2192 ${dir || "."} ${c.dim("(all files here)")}`,
|
|
2973
|
-
`Entire Tool \u2192 * ${c.dim(`(always allow '${tool.name}')`)}`
|
|
2974
|
-
];
|
|
2975
3007
|
const displayOpts = [
|
|
2976
3008
|
`Specific paths \u2192 ${rel || tool.name}`,
|
|
2977
3009
|
`Complete directory \u2192 ${dir || "."}`,
|
|
2978
|
-
`Entire Tool \u2192
|
|
3010
|
+
`Entire Tool \u2192 * (this session)`,
|
|
3011
|
+
`Always, everywhere \u2192 ${tool.name} (never ask again \u2014 saved)`
|
|
2979
3012
|
];
|
|
2980
3013
|
const choice = await pickScope(displayOpts);
|
|
2981
3014
|
if (choice === -1) return "abort";
|
|
@@ -2989,9 +3022,14 @@ ${c.yellow(glyph.caret)} ${describe(tool)}
|
|
|
2989
3022
|
addSessionRule({ tool: tool.name, match, effect: "allow" });
|
|
2990
3023
|
process.stdout.write(` ${c.dim(`Trusted '${tool.name}' for ${match} this session.`)}
|
|
2991
3024
|
`);
|
|
2992
|
-
} else {
|
|
3025
|
+
} else if (choice === 2) {
|
|
2993
3026
|
addSessionRule({ tool: tool.name, match: "*", effect: "allow" });
|
|
2994
3027
|
process.stdout.write(` ${c.dim(`Trusted '${tool.name}' (*) this session.`)}
|
|
3028
|
+
`);
|
|
3029
|
+
} else {
|
|
3030
|
+
addSessionRule({ tool: tool.name, match: "*", effect: "allow" });
|
|
3031
|
+
saveGlobalPermissionRule({ tool: tool.name, match: "*", effect: "allow" });
|
|
3032
|
+
process.stdout.write(` ${c.green(glyph.tick)} ${c.dim(`Always allowing '${tool.name}' everywhere \u2014 saved to ~/.hostwares/permissions.json. You won't be asked again.`)}
|
|
2995
3033
|
`);
|
|
2996
3034
|
}
|
|
2997
3035
|
return "allow";
|
|
@@ -3289,13 +3327,13 @@ init_config();
|
|
|
3289
3327
|
// src/session/store.ts
|
|
3290
3328
|
init_config();
|
|
3291
3329
|
import { createHash } from "crypto";
|
|
3292
|
-
import { join as
|
|
3293
|
-
import { existsSync as
|
|
3294
|
-
var SESSION_DIR =
|
|
3330
|
+
import { join as join8 } from "path";
|
|
3331
|
+
import { existsSync as existsSync8, readFileSync as readFileSync8, readdirSync as readdirSync5, unlinkSync as unlinkSync2, mkdirSync as mkdirSync6 } from "fs";
|
|
3332
|
+
var SESSION_DIR = join8(HW_DIR, "sessions");
|
|
3295
3333
|
var SCHEMA_VERSION = 2;
|
|
3296
3334
|
function sessionPath(cwd) {
|
|
3297
3335
|
const hash = createHash("sha256").update(cwd).digest("hex").slice(0, 24);
|
|
3298
|
-
return
|
|
3336
|
+
return join8(SESSION_DIR, `${hash}.json`);
|
|
3299
3337
|
}
|
|
3300
3338
|
function newSession(cwd = process.cwd()) {
|
|
3301
3339
|
const now = (/* @__PURE__ */ new Date()).toISOString();
|
|
@@ -3311,9 +3349,9 @@ function newSession(cwd = process.cwd()) {
|
|
|
3311
3349
|
}
|
|
3312
3350
|
function loadSession(cwd = process.cwd()) {
|
|
3313
3351
|
const file = sessionPath(cwd);
|
|
3314
|
-
if (!
|
|
3352
|
+
if (!existsSync8(file)) return null;
|
|
3315
3353
|
try {
|
|
3316
|
-
const data = JSON.parse(
|
|
3354
|
+
const data = JSON.parse(readFileSync8(file, "utf8"));
|
|
3317
3355
|
if (data.version !== SCHEMA_VERSION) return null;
|
|
3318
3356
|
if (data.cwd !== cwd) return null;
|
|
3319
3357
|
if (!Array.isArray(data.turns)) return null;
|
|
@@ -3335,7 +3373,7 @@ function isTurn(t) {
|
|
|
3335
3373
|
}
|
|
3336
3374
|
var MAX_LOCAL_TURNS = 200;
|
|
3337
3375
|
function saveSession(session) {
|
|
3338
|
-
|
|
3376
|
+
mkdirSync6(SESSION_DIR, { recursive: true, mode: 448 });
|
|
3339
3377
|
session.updatedAt = (/* @__PURE__ */ new Date()).toISOString();
|
|
3340
3378
|
if (session.turns.length > MAX_LOCAL_TURNS) {
|
|
3341
3379
|
session.turns = session.turns.slice(-MAX_LOCAL_TURNS);
|
|
@@ -3346,12 +3384,12 @@ function appendTurn(session, role, content) {
|
|
|
3346
3384
|
session.turns.push({ role, content, at: (/* @__PURE__ */ new Date()).toISOString() });
|
|
3347
3385
|
}
|
|
3348
3386
|
function listSessions() {
|
|
3349
|
-
if (!
|
|
3387
|
+
if (!existsSync8(SESSION_DIR)) return [];
|
|
3350
3388
|
const out = [];
|
|
3351
3389
|
for (const name of readdirSync5(SESSION_DIR)) {
|
|
3352
3390
|
if (!name.endsWith(".json")) continue;
|
|
3353
3391
|
try {
|
|
3354
|
-
const data = JSON.parse(
|
|
3392
|
+
const data = JSON.parse(readFileSync8(join8(SESSION_DIR, name), "utf8"));
|
|
3355
3393
|
if (data.version !== SCHEMA_VERSION || !data.cwd) continue;
|
|
3356
3394
|
out.push({
|
|
3357
3395
|
cwd: data.cwd,
|
|
@@ -3894,7 +3932,7 @@ async function listSites() {
|
|
|
3894
3932
|
function readVersion() {
|
|
3895
3933
|
try {
|
|
3896
3934
|
const here = dirname4(fileURLToPath(import.meta.url));
|
|
3897
|
-
const pkg = JSON.parse(
|
|
3935
|
+
const pkg = JSON.parse(readFileSync9(join9(here, "..", "package.json"), "utf8"));
|
|
3898
3936
|
return pkg.version ?? "0.0.0";
|
|
3899
3937
|
} catch {
|
|
3900
3938
|
return "0.0.0";
|