unforgit 0.3.2 → 0.4.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/dist/index.js +112 -12
- package/dist/index.js.map +1 -1
- package/package.json +4 -4
package/dist/index.js
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
2
|
|
|
3
3
|
// src/index.ts
|
|
4
|
-
import { Command as
|
|
4
|
+
import { Command as Command30 } from "commander";
|
|
5
5
|
|
|
6
6
|
// src/commands/init.ts
|
|
7
7
|
import fs2 from "fs";
|
|
@@ -3108,14 +3108,111 @@ Examples:
|
|
|
3108
3108
|
}
|
|
3109
3109
|
});
|
|
3110
3110
|
|
|
3111
|
-
// src/commands/
|
|
3111
|
+
// src/commands/dashboard.ts
|
|
3112
|
+
import { spawn as spawn2 } from "child_process";
|
|
3113
|
+
import fs5 from "fs";
|
|
3114
|
+
import path4 from "path";
|
|
3115
|
+
import { fileURLToPath } from "url";
|
|
3112
3116
|
import { Command as Command26 } from "commander";
|
|
3117
|
+
function assertSafeDashboardBind(host, allowNetwork = false) {
|
|
3118
|
+
if ((host === "0.0.0.0" || host === "::") && !allowNetwork) {
|
|
3119
|
+
throw new Error(
|
|
3120
|
+
`Refusing to bind dashboard to ${host} without --allow-network. Use a specific Tailscale/LAN IP when possible.`
|
|
3121
|
+
);
|
|
3122
|
+
}
|
|
3123
|
+
}
|
|
3124
|
+
function parseDashboardPort(port) {
|
|
3125
|
+
const value = port === void 0 ? 3838 : Number(port);
|
|
3126
|
+
if (!Number.isInteger(value) || value < 1 || value > 65535) {
|
|
3127
|
+
throw new Error("--port must be between 1 and 65535");
|
|
3128
|
+
}
|
|
3129
|
+
return value;
|
|
3130
|
+
}
|
|
3131
|
+
function buildDashboardLaunchOptions(input) {
|
|
3132
|
+
const host = input.host ?? "127.0.0.1";
|
|
3133
|
+
assertSafeDashboardBind(host, input.allowNetwork ?? false);
|
|
3134
|
+
const port = parseDashboardPort(input.port);
|
|
3135
|
+
const workspace = path4.resolve(input.workspace ?? input.cwd);
|
|
3136
|
+
return {
|
|
3137
|
+
host,
|
|
3138
|
+
port,
|
|
3139
|
+
workspace,
|
|
3140
|
+
url: `http://${host}:${port}`
|
|
3141
|
+
};
|
|
3142
|
+
}
|
|
3143
|
+
function resolveDashboardAppDir(moduleUrl = import.meta.url) {
|
|
3144
|
+
if (process.env.UNFORGIT_DASHBOARD_APP_DIR) {
|
|
3145
|
+
return path4.resolve(process.env.UNFORGIT_DASHBOARD_APP_DIR);
|
|
3146
|
+
}
|
|
3147
|
+
const thisFile = fileURLToPath(moduleUrl);
|
|
3148
|
+
const dir = path4.dirname(thisFile);
|
|
3149
|
+
if (path4.basename(dir) === "dist") {
|
|
3150
|
+
return path4.resolve(dir, "../../web");
|
|
3151
|
+
}
|
|
3152
|
+
return path4.resolve(dir, "../../../web");
|
|
3153
|
+
}
|
|
3154
|
+
var dashboardCommand = new Command26("dashboard").description("Start the local Unforgit memory dashboard").option("--host <host>", "Host/IP to bind (default: 127.0.0.1)").option("--port <port>", "Port to listen on (default: 3838)").option("--workspace <path>", "Workspace containing .unforgit/ (default: current directory)").option(
|
|
3155
|
+
"--allow-network",
|
|
3156
|
+
"Allow wildcard binds such as 0.0.0.0. Prefer a specific Tailscale/LAN IP instead."
|
|
3157
|
+
).addHelpText("after", `
|
|
3158
|
+
Examples:
|
|
3159
|
+
unforgit dashboard
|
|
3160
|
+
unforgit dashboard --port 4848
|
|
3161
|
+
unforgit dashboard --workspace ~/.hermes/unforgit-memory --host 100.81.12.32`).action((opts) => {
|
|
3162
|
+
let launch;
|
|
3163
|
+
try {
|
|
3164
|
+
launch = buildDashboardLaunchOptions({
|
|
3165
|
+
cwd: process.cwd(),
|
|
3166
|
+
host: opts.host,
|
|
3167
|
+
port: opts.port,
|
|
3168
|
+
workspace: opts.workspace,
|
|
3169
|
+
allowNetwork: opts.allowNetwork
|
|
3170
|
+
});
|
|
3171
|
+
} catch (err) {
|
|
3172
|
+
logger.error(err instanceof Error ? err.message : String(err));
|
|
3173
|
+
process.exit(EXIT_ERROR);
|
|
3174
|
+
}
|
|
3175
|
+
const dashboardAppDir = resolveDashboardAppDir();
|
|
3176
|
+
if (!fs5.existsSync(path4.join(dashboardAppDir, "package.json"))) {
|
|
3177
|
+
logger.error(`Dashboard app not found at ${dashboardAppDir}.`);
|
|
3178
|
+
logger.error("Set UNFORGIT_DASHBOARD_APP_DIR to the apps/web directory.");
|
|
3179
|
+
process.exit(EXIT_ERROR);
|
|
3180
|
+
}
|
|
3181
|
+
logger.info(`Starting Unforgit dashboard: ${launch.url}`);
|
|
3182
|
+
logger.info(`Workspace: ${launch.workspace}`);
|
|
3183
|
+
if (launch.host !== "127.0.0.1" && launch.host !== "localhost") {
|
|
3184
|
+
logger.warn("Dashboard is bound to a network address. Use only on trusted private networks such as Tailscale.");
|
|
3185
|
+
}
|
|
3186
|
+
const child = spawn2(
|
|
3187
|
+
"pnpm",
|
|
3188
|
+
["exec", "next", "dev", "-p", String(launch.port), "-H", launch.host],
|
|
3189
|
+
{
|
|
3190
|
+
cwd: dashboardAppDir,
|
|
3191
|
+
stdio: "inherit",
|
|
3192
|
+
env: {
|
|
3193
|
+
...process.env,
|
|
3194
|
+
UNFORGIT_WORKSPACE: launch.workspace
|
|
3195
|
+
}
|
|
3196
|
+
}
|
|
3197
|
+
);
|
|
3198
|
+
child.on("error", (err) => {
|
|
3199
|
+
logger.error(`Failed to start dashboard: ${err.message}`);
|
|
3200
|
+
process.exit(EXIT_ERROR);
|
|
3201
|
+
});
|
|
3202
|
+
child.on("exit", (code, signal) => {
|
|
3203
|
+
if (signal) process.kill(process.pid, signal);
|
|
3204
|
+
process.exit(code ?? 0);
|
|
3205
|
+
});
|
|
3206
|
+
});
|
|
3207
|
+
|
|
3208
|
+
// src/commands/doctor.ts
|
|
3209
|
+
import { Command as Command27 } from "commander";
|
|
3113
3210
|
import { loadConfig as loadConfig22, getDbPath as getDbPath20, isInitialized as isInitialized16, getConfigPath as getConfigPath2 } from "unforgit-config";
|
|
3114
3211
|
import { LocalStore as LocalStore19 } from "unforgit-db";
|
|
3115
3212
|
import { RemoteClient as RemoteClient13 } from "unforgit-config";
|
|
3116
|
-
import
|
|
3213
|
+
import fs6 from "fs";
|
|
3117
3214
|
import YAML from "yaml";
|
|
3118
|
-
var doctorCommand = new
|
|
3215
|
+
var doctorCommand = new Command27("doctor").description("Check system health and diagnose common issues").action(async () => {
|
|
3119
3216
|
const results = [];
|
|
3120
3217
|
if (!isInitialized16()) {
|
|
3121
3218
|
results.push({
|
|
@@ -3149,7 +3246,7 @@ var doctorCommand = new Command26("doctor").description("Check system health and
|
|
|
3149
3246
|
}
|
|
3150
3247
|
if (process.platform !== "win32") {
|
|
3151
3248
|
try {
|
|
3152
|
-
const stat =
|
|
3249
|
+
const stat = fs6.statSync(configPath);
|
|
3153
3250
|
const mode = stat.mode & 511;
|
|
3154
3251
|
if (mode & 36) {
|
|
3155
3252
|
results.push({
|
|
@@ -3325,7 +3422,7 @@ function exitForResults(results) {
|
|
|
3325
3422
|
}
|
|
3326
3423
|
function findDeprecatedConfigKeys(configPath) {
|
|
3327
3424
|
try {
|
|
3328
|
-
const raw =
|
|
3425
|
+
const raw = fs6.readFileSync(configPath, "utf-8");
|
|
3329
3426
|
const parsed = YAML.parse(raw) ?? {};
|
|
3330
3427
|
const deprecated = [];
|
|
3331
3428
|
if (parsed.remote?.apiKey) {
|
|
@@ -3361,14 +3458,14 @@ function printResults(results) {
|
|
|
3361
3458
|
}
|
|
3362
3459
|
|
|
3363
3460
|
// src/commands/completion.ts
|
|
3364
|
-
import { Command as
|
|
3461
|
+
import { Command as Command28 } from "commander";
|
|
3365
3462
|
var BASH_COMPLETION = `
|
|
3366
3463
|
_unforgit_completions() {
|
|
3367
3464
|
local cur prev commands
|
|
3368
3465
|
COMPREPLY=()
|
|
3369
3466
|
cur="\${COMP_WORDS[COMP_CWORD]}"
|
|
3370
3467
|
prev="\${COMP_WORDS[COMP_CWORD-1]}"
|
|
3371
|
-
commands="init add recall promote consolidate deprecate supersede delete restore web link unlink links merge remerge similar history auto-consolidate unconsolidate status push pull remote log diff keys auth config embeddings reset doctor curate completion"
|
|
3468
|
+
commands="init add recall promote consolidate deprecate supersede delete restore web dashboard link unlink links merge remerge similar history auto-consolidate unconsolidate status push pull remote log diff keys auth config embeddings reset doctor curate completion"
|
|
3372
3469
|
|
|
3373
3470
|
case "\${prev}" in
|
|
3374
3471
|
unforgit)
|
|
@@ -3424,6 +3521,7 @@ _unforgit() {
|
|
|
3424
3521
|
'delete:Soft delete a memory'
|
|
3425
3522
|
'restore:Restore a soft-deleted memory'
|
|
3426
3523
|
'web:Start the web dashboard'
|
|
3524
|
+
'dashboard:Start the local memory dashboard'
|
|
3427
3525
|
'link:Create a link between memories'
|
|
3428
3526
|
'unlink:Remove a link between memories'
|
|
3429
3527
|
'links:List links for a memory'
|
|
@@ -3479,6 +3577,7 @@ complete -c unforgit -n '__fish_use_subcommand' -a 'supersede' -d 'Supersede a m
|
|
|
3479
3577
|
complete -c unforgit -n '__fish_use_subcommand' -a 'delete' -d 'Delete a memory'
|
|
3480
3578
|
complete -c unforgit -n '__fish_use_subcommand' -a 'restore' -d 'Restore a memory'
|
|
3481
3579
|
complete -c unforgit -n '__fish_use_subcommand' -a 'web' -d 'Start web dashboard'
|
|
3580
|
+
complete -c unforgit -n '__fish_use_subcommand' -a 'dashboard' -d 'Start local memory dashboard'
|
|
3482
3581
|
complete -c unforgit -n '__fish_use_subcommand' -a 'link' -d 'Link two memories'
|
|
3483
3582
|
complete -c unforgit -n '__fish_use_subcommand' -a 'unlink' -d 'Unlink memories'
|
|
3484
3583
|
complete -c unforgit -n '__fish_use_subcommand' -a 'links' -d 'List links'
|
|
@@ -3506,7 +3605,7 @@ complete -c unforgit -l verbose -d 'Enable verbose output'
|
|
|
3506
3605
|
complete -c unforgit -l quiet -d 'Suppress non-essential output'
|
|
3507
3606
|
complete -c unforgit -l json -d 'Output as JSON'
|
|
3508
3607
|
`.trim();
|
|
3509
|
-
var completionCommand = new
|
|
3608
|
+
var completionCommand = new Command28("completion").description("Generate shell completion scripts").argument("<shell>", "Shell type (bash, zsh, fish)").addHelpText("after", `
|
|
3510
3609
|
Examples:
|
|
3511
3610
|
unforgit completion bash >> ~/.bashrc
|
|
3512
3611
|
unforgit completion zsh >> ~/.zshrc
|
|
@@ -3528,7 +3627,7 @@ Examples:
|
|
|
3528
3627
|
});
|
|
3529
3628
|
|
|
3530
3629
|
// src/commands/curate.ts
|
|
3531
|
-
import { Command as
|
|
3630
|
+
import { Command as Command29 } from "commander";
|
|
3532
3631
|
import { getDbPath as getDbPath21, isInitialized as isInitialized17, loadConfig as loadConfig23 } from "unforgit-config";
|
|
3533
3632
|
import { LocalStore as LocalStore20 } from "unforgit-db";
|
|
3534
3633
|
import { RemoteClient as RemoteClient14 } from "unforgit-config";
|
|
@@ -3545,7 +3644,7 @@ function formatCandidatePreview2(candidate) {
|
|
|
3545
3644
|
}
|
|
3546
3645
|
return lines.join("\n");
|
|
3547
3646
|
}
|
|
3548
|
-
var curateCommand = new
|
|
3647
|
+
var curateCommand = new Command29("curate").description("Preview or run lifecycle maintenance for repository memories").option("--remote", "Run maintenance against the remote server").option("--execute", "Apply changes instead of previewing them").option("--model <model>", "OpenAI model to use for consolidation").option("--no-preserve", "Do not preserve original memories during consolidation").addHelpText("after", `
|
|
3549
3648
|
Examples:
|
|
3550
3649
|
unforgit curate
|
|
3551
3650
|
unforgit curate --execute
|
|
@@ -3679,7 +3778,7 @@ process.on("unhandledRejection", (err) => {
|
|
|
3679
3778
|
runCleanup();
|
|
3680
3779
|
process.exit(EXIT_ERROR);
|
|
3681
3780
|
});
|
|
3682
|
-
var program = new
|
|
3781
|
+
var program = new Command30();
|
|
3683
3782
|
program.name("unforgit").description("Unforgit \u2014 repository memory for agents and developers").version(pkg.version).option("--verbose", "Enable verbose output").option("--quiet", "Suppress non-essential output").option("--json", "Output results as JSON (for scripting)").hook("preAction", () => {
|
|
3684
3783
|
const opts = program.opts();
|
|
3685
3784
|
if (opts.quiet) setVerbosity(0);
|
|
@@ -3717,6 +3816,7 @@ program.addCommand(configCommand);
|
|
|
3717
3816
|
program.addCommand(embeddingsCommand);
|
|
3718
3817
|
program.addCommand(resetCommand);
|
|
3719
3818
|
program.addCommand(backupsCommand);
|
|
3819
|
+
program.addCommand(dashboardCommand);
|
|
3720
3820
|
program.addCommand(doctorCommand);
|
|
3721
3821
|
program.addCommand(curateCommand);
|
|
3722
3822
|
program.addCommand(completionCommand);
|