notoken-core 1.5.0 → 1.6.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.
@@ -1037,13 +1037,17 @@
1037
1037
  "ls",
1038
1038
  "show files",
1039
1039
  "directory listing",
1040
- "what files"
1040
+ "what files",
1041
+ "do an ls",
1042
+ "what is in this folder",
1043
+ "what's here",
1044
+ "show directory"
1041
1045
  ],
1042
1046
  "fields": {
1043
1047
  "path": {
1044
1048
  "type": "string",
1045
1049
  "required": false,
1046
- "default": "/var/log"
1050
+ "default": "."
1047
1051
  },
1048
1052
  "environment": {
1049
1053
  "type": "environment",
@@ -3915,6 +3919,97 @@
3915
3919
  "convex deploy",
3916
3920
  "convex run"
3917
3921
  ]
3922
+ },
3923
+
3924
+ {
3925
+ "name": "project.scan",
3926
+ "description": "Scan for software projects in a directory",
3927
+ "synonyms": ["scan projects", "what projects", "find projects", "check what projects", "scan for projects", "check projects", "project scan"],
3928
+ "fields": {
3929
+ "path": { "type": "string", "required": false, "default": "." }
3930
+ },
3931
+ "command": "echo '=== Scanning for projects ===' && find {{path}} -maxdepth 3 \\( -name 'package.json' -o -name 'Cargo.toml' -o -name 'go.mod' -o -name 'requirements.txt' -o -name 'pyproject.toml' -o -name 'Gemfile' -o -name 'pom.xml' -o -name 'build.gradle' -o -name 'composer.json' -o -name '*.sln' -o -name '*.csproj' -o -name 'Dockerfile' -o -name 'docker-compose.yml' -o -name 'docker-compose.yaml' \\) -not -path '*/node_modules/*' -not -path '*/.git/*' -not -path '*/vendor/*' -not -path '*/target/*' 2>/dev/null | sort",
3932
+ "execution": "local",
3933
+ "requiresConfirmation": false,
3934
+ "riskLevel": "low",
3935
+ "examples": [
3936
+ "scan for projects",
3937
+ "what projects are on here",
3938
+ "check what projects are in this folder",
3939
+ "find projects"
3940
+ ]
3941
+ },
3942
+ {
3943
+ "name": "project.info",
3944
+ "description": "Show info about the current project (type, deps, scripts)",
3945
+ "synonyms": ["project info", "what project is this", "what kind of project", "project type", "what framework", "what stack"],
3946
+ "fields": {
3947
+ "path": { "type": "string", "required": false, "default": "." }
3948
+ },
3949
+ "command": "echo '=== Project Info ===' && ls {{path}}/package.json 2>/dev/null && echo 'Node.js project' && cat {{path}}/package.json 2>/dev/null | head -20 || ls {{path}}/Cargo.toml 2>/dev/null && echo 'Rust project' || ls {{path}}/go.mod 2>/dev/null && echo 'Go project' || ls {{path}}/requirements.txt 2>/dev/null && echo 'Python project' || ls {{path}}/composer.json 2>/dev/null && echo 'PHP project' || echo 'Unknown project type'",
3950
+ "execution": "local",
3951
+ "requiresConfirmation": false,
3952
+ "riskLevel": "low",
3953
+ "examples": [
3954
+ "what project is this",
3955
+ "project info",
3956
+ "what framework is this"
3957
+ ]
3958
+ },
3959
+ {
3960
+ "name": "openclaw.start",
3961
+ "description": "Start the OpenClaw gateway",
3962
+ "synonyms": ["start openclaw", "openclaw start", "start gateway", "openclaw gateway", "run openclaw"],
3963
+ "fields": {},
3964
+ "command": "openclaw gateway --verbose",
3965
+ "execution": "local",
3966
+ "requiresConfirmation": false,
3967
+ "riskLevel": "low",
3968
+ "examples": ["start openclaw", "run the gateway", "openclaw start"]
3969
+ },
3970
+ {
3971
+ "name": "openclaw.stop",
3972
+ "description": "Stop the OpenClaw gateway",
3973
+ "synonyms": ["stop openclaw", "openclaw stop", "stop gateway", "kill openclaw"],
3974
+ "fields": {},
3975
+ "command": "pkill -f 'openclaw gateway' 2>/dev/null && echo 'Gateway stopped' || echo 'Gateway not running'",
3976
+ "execution": "local",
3977
+ "requiresConfirmation": true,
3978
+ "riskLevel": "medium",
3979
+ "examples": ["stop openclaw", "stop the gateway"]
3980
+ },
3981
+ {
3982
+ "name": "openclaw.restart",
3983
+ "description": "Restart the OpenClaw gateway",
3984
+ "synonyms": ["restart openclaw", "openclaw restart", "restart gateway", "bounce openclaw"],
3985
+ "fields": {},
3986
+ "command": "pkill -f 'openclaw gateway' 2>/dev/null; sleep 1; openclaw gateway --verbose &",
3987
+ "execution": "local",
3988
+ "requiresConfirmation": true,
3989
+ "riskLevel": "medium",
3990
+ "examples": ["restart openclaw", "restart the gateway"]
3991
+ },
3992
+ {
3993
+ "name": "openclaw.status",
3994
+ "description": "Check OpenClaw gateway status and channels",
3995
+ "synonyms": ["openclaw status", "openclaw check", "is openclaw running", "check openclaw", "gateway status"],
3996
+ "fields": {},
3997
+ "command": "echo '=== OpenClaw Status ===' && openclaw --version 2>&1 && echo '' && openclaw channels status 2>&1 && echo '' && openclaw channels list 2>&1 || echo 'OpenClaw not available'",
3998
+ "execution": "local",
3999
+ "requiresConfirmation": false,
4000
+ "riskLevel": "low",
4001
+ "examples": ["openclaw status", "is openclaw running", "check openclaw"]
4002
+ },
4003
+ {
4004
+ "name": "openclaw.doctor",
4005
+ "description": "Run OpenClaw doctor to diagnose and fix issues",
4006
+ "synonyms": ["openclaw doctor", "fix openclaw", "diagnose openclaw", "openclaw fix"],
4007
+ "fields": {},
4008
+ "command": "openclaw doctor --fix 2>&1 || echo 'OpenClaw not available — run: notoken setup openclaw'",
4009
+ "execution": "local",
4010
+ "requiresConfirmation": false,
4011
+ "riskLevel": "low",
4012
+ "examples": ["openclaw doctor", "fix openclaw", "diagnose openclaw"]
3918
4013
  }
3919
4014
  ]
3920
4015
  }
package/dist/index.d.ts CHANGED
@@ -47,6 +47,7 @@ export { createBackup, rollback, listBackups, cleanExpiredBackups, formatBackupL
47
47
  export { checkForUpdate, checkForUpdateSync, runUpdate, formatUpdateBanner, type UpdateInfo } from "./utils/updater.js";
48
48
  export { detectProviders, formatStatus, goOffline, goOnline, disableLLM, enableLLM, isOfflineMode, isLLMDisabled, recordOfflineCommand, getTokensSaved, formatTokensSaved, formatTokensSavedBrief, saveOnExit, getSessionId, type LLMProvider, type LLMState, } from "./utils/llmManager.js";
49
49
  export { getRecentSessions, getSessionsForFolder, formatSessionSummary, formatSessionList, type SessionSummary, } from "./utils/sessionSummary.js";
50
+ export { isSessionOpen, toggleSession, hideSession, unhideSession, getHiddenSessions, getLastViewedSession, createFullBackup, restoreFromBackup, listFullBackups, formatBackupsList, type BackupInfo, } from "./utils/sessionBackup.js";
50
51
  export { logFailure, loadFailures, clearFailures } from "./utils/logger.js";
51
52
  export { logUncertainty, loadUncertaintyLog, getUncertaintySummary } from "./nlp/uncertainty.js";
52
53
  export { recordHistory, loadHistory, getRecentHistory, searchHistory } from "./context/history.js";
package/dist/index.js CHANGED
@@ -62,6 +62,8 @@ export { checkForUpdate, checkForUpdateSync, runUpdate, formatUpdateBanner } fro
62
62
  export { detectProviders, formatStatus, goOffline, goOnline, disableLLM, enableLLM, isOfflineMode, isLLMDisabled, recordOfflineCommand, getTokensSaved, formatTokensSaved, formatTokensSavedBrief, saveOnExit, getSessionId, } from "./utils/llmManager.js";
63
63
  // ── Session Summaries ──
64
64
  export { getRecentSessions, getSessionsForFolder, formatSessionSummary, formatSessionList, } from "./utils/sessionSummary.js";
65
+ // ── Session Backup & Prefs ──
66
+ export { isSessionOpen, toggleSession, hideSession, unhideSession, getHiddenSessions, getLastViewedSession, createFullBackup, restoreFromBackup, listFullBackups, formatBackupsList, } from "./utils/sessionBackup.js";
65
67
  // ── Logging ──
66
68
  export { logFailure, loadFailures, clearFailures } from "./utils/logger.js";
67
69
  export { logUncertainty, loadUncertaintyLog, getUncertaintySummary } from "./nlp/uncertainty.js";
@@ -0,0 +1,40 @@
1
+ /**
2
+ * Session backup and restore.
3
+ *
4
+ * Backup: tars ~/.notoken/ into a timestamped archive
5
+ * Restore: extracts an archive back to ~/.notoken/
6
+ * Manages open/closed state per session for the dashboard
7
+ */
8
+ export declare function isSessionOpen(sessionId: string): boolean;
9
+ export declare function toggleSession(sessionId: string): boolean;
10
+ export declare function hideSession(sessionId: string): void;
11
+ export declare function unhideSession(sessionId: string): void;
12
+ export declare function getHiddenSessions(): string[];
13
+ export declare function getLastViewedSession(): string | undefined;
14
+ export interface BackupInfo {
15
+ path: string;
16
+ filename: string;
17
+ size: string;
18
+ createdAt: string;
19
+ }
20
+ /**
21
+ * Create a full backup of ~/.notoken/ as a tar.gz archive.
22
+ * Saves to ~/.notoken/backups/ by default, or a custom path.
23
+ */
24
+ export declare function createFullBackup(outputDir?: string): BackupInfo;
25
+ /**
26
+ * Restore from a backup archive.
27
+ * Creates a safety backup of current state first.
28
+ */
29
+ export declare function restoreFromBackup(archivePath: string): {
30
+ success: boolean;
31
+ message: string;
32
+ };
33
+ /**
34
+ * List available backups.
35
+ */
36
+ export declare function listFullBackups(): BackupInfo[];
37
+ /**
38
+ * Format backup list for display.
39
+ */
40
+ export declare function formatBackupsList(backups: BackupInfo[]): string;
@@ -0,0 +1,181 @@
1
+ /**
2
+ * Session backup and restore.
3
+ *
4
+ * Backup: tars ~/.notoken/ into a timestamped archive
5
+ * Restore: extracts an archive back to ~/.notoken/
6
+ * Manages open/closed state per session for the dashboard
7
+ */
8
+ import { execSync } from "node:child_process";
9
+ import { existsSync, readFileSync, writeFileSync, mkdirSync } from "node:fs";
10
+ import { resolve, basename } from "node:path";
11
+ import { USER_HOME } from "./paths.js";
12
+ const PREFS_FILE = resolve(USER_HOME, "session-prefs.json");
13
+ const BACKUP_DIR = resolve(USER_HOME, "backups");
14
+ function loadPrefs() {
15
+ try {
16
+ if (existsSync(PREFS_FILE))
17
+ return JSON.parse(readFileSync(PREFS_FILE, "utf-8"));
18
+ }
19
+ catch { }
20
+ return { openSessions: [], hiddenSessions: [] };
21
+ }
22
+ function savePrefs(prefs) {
23
+ try {
24
+ mkdirSync(USER_HOME, { recursive: true });
25
+ writeFileSync(PREFS_FILE, JSON.stringify(prefs, null, 2));
26
+ }
27
+ catch { }
28
+ }
29
+ export function isSessionOpen(sessionId) {
30
+ return loadPrefs().openSessions.includes(sessionId);
31
+ }
32
+ export function toggleSession(sessionId) {
33
+ const prefs = loadPrefs();
34
+ const idx = prefs.openSessions.indexOf(sessionId);
35
+ if (idx >= 0) {
36
+ prefs.openSessions.splice(idx, 1);
37
+ savePrefs(prefs);
38
+ return false;
39
+ }
40
+ else {
41
+ prefs.openSessions.push(sessionId);
42
+ prefs.lastViewed = sessionId;
43
+ savePrefs(prefs);
44
+ return true;
45
+ }
46
+ }
47
+ export function hideSession(sessionId) {
48
+ const prefs = loadPrefs();
49
+ if (!prefs.hiddenSessions.includes(sessionId)) {
50
+ prefs.hiddenSessions.push(sessionId);
51
+ }
52
+ prefs.openSessions = prefs.openSessions.filter(s => s !== sessionId);
53
+ savePrefs(prefs);
54
+ }
55
+ export function unhideSession(sessionId) {
56
+ const prefs = loadPrefs();
57
+ prefs.hiddenSessions = prefs.hiddenSessions.filter(s => s !== sessionId);
58
+ savePrefs(prefs);
59
+ }
60
+ export function getHiddenSessions() {
61
+ return loadPrefs().hiddenSessions;
62
+ }
63
+ export function getLastViewedSession() {
64
+ return loadPrefs().lastViewed;
65
+ }
66
+ /**
67
+ * Create a full backup of ~/.notoken/ as a tar.gz archive.
68
+ * Saves to ~/.notoken/backups/ by default, or a custom path.
69
+ */
70
+ export function createFullBackup(outputDir) {
71
+ const dir = outputDir ?? BACKUP_DIR;
72
+ mkdirSync(dir, { recursive: true });
73
+ const ts = new Date().toISOString().replace(/[:.]/g, "-").slice(0, 19);
74
+ const filename = `notoken-backup-${ts}.tar.gz`;
75
+ const fullPath = resolve(dir, filename);
76
+ // Tar everything except the backups directory itself
77
+ execSync(`tar -czf "${fullPath}" -C "${resolve(USER_HOME, "..")}" --exclude="backups" "${basename(USER_HOME)}"`, { timeout: 60_000, stdio: "pipe" });
78
+ const size = tryExec(`ls -lh "${fullPath}" | awk '{print $5}'`) ?? "unknown";
79
+ return {
80
+ path: fullPath,
81
+ filename,
82
+ size,
83
+ createdAt: new Date().toISOString(),
84
+ };
85
+ }
86
+ /**
87
+ * Restore from a backup archive.
88
+ * Creates a safety backup of current state first.
89
+ */
90
+ export function restoreFromBackup(archivePath) {
91
+ if (!existsSync(archivePath)) {
92
+ return { success: false, message: `Backup not found: ${archivePath}` };
93
+ }
94
+ // Safety backup of current state
95
+ try {
96
+ const safety = createFullBackup();
97
+ console.error(`\x1b[2m[backup] Safety backup created: ${safety.path}\x1b[0m`);
98
+ }
99
+ catch { }
100
+ // Extract
101
+ try {
102
+ execSync(`tar -xzf "${archivePath}" -C "${resolve(USER_HOME, "..")}"`, { timeout: 60_000, stdio: "pipe" });
103
+ return { success: true, message: `Restored from ${basename(archivePath)}` };
104
+ }
105
+ catch (err) {
106
+ return { success: false, message: `Restore failed: ${err.message}` };
107
+ }
108
+ }
109
+ /**
110
+ * List available backups.
111
+ */
112
+ export function listFullBackups() {
113
+ if (!existsSync(BACKUP_DIR))
114
+ return [];
115
+ try {
116
+ const { readdirSync, statSync } = require("node:fs");
117
+ return readdirSync(BACKUP_DIR)
118
+ .filter((f) => f.startsWith("notoken-backup-") && f.endsWith(".tar.gz"))
119
+ .map((f) => {
120
+ const full = resolve(BACKUP_DIR, f);
121
+ const stat = statSync(full);
122
+ return {
123
+ path: full,
124
+ filename: f,
125
+ size: formatSize(stat.size),
126
+ createdAt: stat.mtime.toISOString(),
127
+ };
128
+ })
129
+ .sort((a, b) => b.createdAt.localeCompare(a.createdAt));
130
+ }
131
+ catch {
132
+ return [];
133
+ }
134
+ }
135
+ /**
136
+ * Format backup list for display.
137
+ */
138
+ export function formatBackupsList(backups) {
139
+ const c = { reset: "\x1b[0m", bold: "\x1b[1m", dim: "\x1b[2m", cyan: "\x1b[36m", green: "\x1b[32m" };
140
+ if (backups.length === 0)
141
+ return `${c.dim}No backups found.${c.reset}`;
142
+ const lines = [`${c.bold}Backups:${c.reset}\n`];
143
+ for (const b of backups) {
144
+ const ago = timeAgo(b.createdAt);
145
+ lines.push(` ${c.cyan}${b.filename}${c.reset} — ${b.size} — ${ago}`);
146
+ }
147
+ lines.push(`\n ${c.dim}Backup dir: ${BACKUP_DIR}${c.reset}`);
148
+ lines.push(` ${c.dim}Restore: notoken restore <filename>${c.reset}`);
149
+ return lines.join("\n");
150
+ }
151
+ // ─── Helpers ────────────────────────────────────────────────────────────────
152
+ function formatSize(bytes) {
153
+ if (bytes < 1024)
154
+ return `${bytes} B`;
155
+ if (bytes < 1048576)
156
+ return `${(bytes / 1024).toFixed(1)} KB`;
157
+ if (bytes < 1073741824)
158
+ return `${(bytes / 1048576).toFixed(1)} MB`;
159
+ return `${(bytes / 1073741824).toFixed(1)} GB`;
160
+ }
161
+ function timeAgo(dateStr) {
162
+ const ms = Date.now() - new Date(dateStr).getTime();
163
+ const mins = Math.floor(ms / 60000);
164
+ if (mins < 1)
165
+ return "just now";
166
+ if (mins < 60)
167
+ return `${mins}m ago`;
168
+ const hours = Math.floor(mins / 60);
169
+ if (hours < 24)
170
+ return `${hours}h ago`;
171
+ const days = Math.floor(hours / 24);
172
+ return `${days}d ago`;
173
+ }
174
+ function tryExec(cmd) {
175
+ try {
176
+ return execSync(cmd, { encoding: "utf-8", stdio: ["pipe", "pipe", "pipe"], timeout: 10_000 }).trim() || null;
177
+ }
178
+ catch {
179
+ return null;
180
+ }
181
+ }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "notoken-core",
3
- "version": "1.5.0",
3
+ "version": "1.6.0",
4
4
  "description": "Shared engine for notoken — NLP parsing, execution, detection, analysis",
5
5
  "type": "module",
6
6
  "license": "MIT",