notoken-core 1.0.0 → 1.2.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/healing/claudeHealer.d.ts +1 -1
- package/dist/healing/claudeHealer.js +2 -2
- package/dist/healing/ruleBuilder.js +4 -4
- package/dist/index.d.ts +1 -0
- package/dist/index.js +2 -0
- package/dist/nlp/llmFallback.d.ts +2 -2
- package/dist/nlp/llmFallback.js +13 -13
- package/dist/nlp/llmParser.d.ts +1 -1
- package/dist/nlp/llmParser.js +4 -4
- package/dist/utils/paths.d.ts +9 -8
- package/dist/utils/paths.js +10 -11
- package/dist/utils/updater.d.ts +32 -0
- package/dist/utils/updater.js +157 -0
- package/package.json +5 -2
|
@@ -12,7 +12,7 @@
|
|
|
12
12
|
*
|
|
13
13
|
* Usage:
|
|
14
14
|
* npx tsx src/healing/claudeHealer.ts [--promote] [--dry-run]
|
|
15
|
-
*
|
|
15
|
+
* NOTOKEN_LLM_CLI=claude npm run heal:claude
|
|
16
16
|
*/
|
|
17
17
|
import { execSync, execFileSync } from "node:child_process";
|
|
18
18
|
import { readFileSync, existsSync } from "node:fs";
|
|
@@ -34,7 +34,7 @@ async function main() {
|
|
|
34
34
|
execSync("command -v claude", { stdio: "pipe" });
|
|
35
35
|
}
|
|
36
36
|
catch {
|
|
37
|
-
console.error(`${c.red}Claude CLI not found. Install it or set
|
|
37
|
+
console.error(`${c.red}Claude CLI not found. Install it or set NOTOKEN_LLM_CLI=claude.${c.reset}`);
|
|
38
38
|
process.exit(1);
|
|
39
39
|
}
|
|
40
40
|
// Gather context
|
|
@@ -4,14 +4,14 @@ import { RulePatch as RulePatchSchema } from "../types/rules.js";
|
|
|
4
4
|
* RuleBuilder: asks an LLM to propose new rules from a set of example phrases.
|
|
5
5
|
*/
|
|
6
6
|
export async function buildRulesFromExamples(examples) {
|
|
7
|
-
const endpoint = process.env.
|
|
7
|
+
const endpoint = process.env.NOTOKEN_LLM_ENDPOINT;
|
|
8
8
|
if (!endpoint) {
|
|
9
|
-
console.error("Set
|
|
9
|
+
console.error("Set NOTOKEN_LLM_ENDPOINT to use the RuleBuilder.");
|
|
10
10
|
return null;
|
|
11
11
|
}
|
|
12
12
|
const rules = loadRules();
|
|
13
13
|
const intents = loadIntents();
|
|
14
|
-
const apiKey = process.env.
|
|
14
|
+
const apiKey = process.env.NOTOKEN_LLM_API_KEY ?? "";
|
|
15
15
|
const intentList = intents.map((i) => `- ${i.name}: ${i.description}`).join("\n");
|
|
16
16
|
const prompt = `You are a rule builder for a CLI command parser.
|
|
17
17
|
|
|
@@ -56,7 +56,7 @@ Rules:
|
|
|
56
56
|
...(apiKey ? { Authorization: `Bearer ${apiKey}`, "x-api-key": apiKey } : {}),
|
|
57
57
|
},
|
|
58
58
|
body: JSON.stringify({
|
|
59
|
-
model: process.env.
|
|
59
|
+
model: process.env.NOTOKEN_LLM_MODEL ?? "claude-sonnet-4-20250514",
|
|
60
60
|
max_tokens: 1024,
|
|
61
61
|
messages: [{ role: "user", content: prompt }],
|
|
62
62
|
}),
|
package/dist/index.d.ts
CHANGED
|
@@ -44,6 +44,7 @@ export { formatExplain } from "./utils/explain.js";
|
|
|
44
44
|
export { formatParsedCommand } from "./utils/output.js";
|
|
45
45
|
export { Spinner, withSpinner, progressBar } from "./utils/spinner.js";
|
|
46
46
|
export { createBackup, rollback, listBackups, cleanExpiredBackups, formatBackupList } from "./utils/autoBackup.js";
|
|
47
|
+
export { checkForUpdate, checkForUpdateSync, runUpdate, formatUpdateBanner, type UpdateInfo } from "./utils/updater.js";
|
|
47
48
|
export { logFailure, loadFailures, clearFailures } from "./utils/logger.js";
|
|
48
49
|
export { logUncertainty, loadUncertaintyLog, getUncertaintySummary } from "./nlp/uncertainty.js";
|
|
49
50
|
export { recordHistory, loadHistory, getRecentHistory, searchHistory } from "./context/history.js";
|
package/dist/index.js
CHANGED
|
@@ -56,6 +56,8 @@ export { formatParsedCommand } from "./utils/output.js";
|
|
|
56
56
|
export { Spinner, withSpinner, progressBar } from "./utils/spinner.js";
|
|
57
57
|
// ── Auto-backup ──
|
|
58
58
|
export { createBackup, rollback, listBackups, cleanExpiredBackups, formatBackupList } from "./utils/autoBackup.js";
|
|
59
|
+
// ── Updates ──
|
|
60
|
+
export { checkForUpdate, checkForUpdateSync, runUpdate, formatUpdateBanner } from "./utils/updater.js";
|
|
59
61
|
// ── Logging ──
|
|
60
62
|
export { logFailure, loadFailures, clearFailures } from "./utils/logger.js";
|
|
61
63
|
export { logUncertainty, loadUncertaintyLog, getUncertaintySummary } from "./nlp/uncertainty.js";
|
|
@@ -2,8 +2,8 @@
|
|
|
2
2
|
* LLM fallback for unrecognized prompts.
|
|
3
3
|
*
|
|
4
4
|
* ONLY fires when an LLM is actually configured:
|
|
5
|
-
* -
|
|
6
|
-
* -
|
|
5
|
+
* - NOTOKEN_LLM_ENDPOINT env var is set (for API), OR
|
|
6
|
+
* - NOTOKEN_LLM_CLI=claude|chatgpt is set (for CLI tools)
|
|
7
7
|
*
|
|
8
8
|
* Otherwise returns null immediately — no noise, no "trying fallback" messages.
|
|
9
9
|
*/
|
package/dist/nlp/llmFallback.js
CHANGED
|
@@ -2,8 +2,8 @@
|
|
|
2
2
|
* LLM fallback for unrecognized prompts.
|
|
3
3
|
*
|
|
4
4
|
* ONLY fires when an LLM is actually configured:
|
|
5
|
-
* -
|
|
6
|
-
* -
|
|
5
|
+
* - NOTOKEN_LLM_ENDPOINT env var is set (for API), OR
|
|
6
|
+
* - NOTOKEN_LLM_CLI=claude|chatgpt is set (for CLI tools)
|
|
7
7
|
*
|
|
8
8
|
* Otherwise returns null immediately — no noise, no "trying fallback" messages.
|
|
9
9
|
*/
|
|
@@ -18,13 +18,13 @@ import { detectLocalPlatform } from "../utils/platform.js";
|
|
|
18
18
|
* Order: explicit config → auto-detect Ollama → nothing.
|
|
19
19
|
*/
|
|
20
20
|
export function isLLMConfigured() {
|
|
21
|
-
return !!(process.env.
|
|
21
|
+
return !!(process.env.NOTOKEN_LLM_ENDPOINT || process.env.NOTOKEN_LLM_CLI || detectOllama());
|
|
22
22
|
}
|
|
23
23
|
/** Which LLM backend is active? */
|
|
24
24
|
export function getLLMBackend() {
|
|
25
|
-
if (process.env.
|
|
26
|
-
return process.env.
|
|
27
|
-
if (process.env.
|
|
25
|
+
if (process.env.NOTOKEN_LLM_CLI)
|
|
26
|
+
return process.env.NOTOKEN_LLM_CLI;
|
|
27
|
+
if (process.env.NOTOKEN_LLM_ENDPOINT)
|
|
28
28
|
return "api";
|
|
29
29
|
if (detectOllama())
|
|
30
30
|
return "ollama";
|
|
@@ -56,13 +56,13 @@ export async function llmFallback(rawText, context) {
|
|
|
56
56
|
if (!isLLMConfigured())
|
|
57
57
|
return null;
|
|
58
58
|
// Try CLI tool if configured
|
|
59
|
-
if (process.env.
|
|
59
|
+
if (process.env.NOTOKEN_LLM_CLI) {
|
|
60
60
|
const cliResult = await tryLLMCli(rawText, context);
|
|
61
61
|
if (cliResult)
|
|
62
62
|
return cliResult;
|
|
63
63
|
}
|
|
64
64
|
// Try API endpoint if configured
|
|
65
|
-
if (process.env.
|
|
65
|
+
if (process.env.NOTOKEN_LLM_ENDPOINT) {
|
|
66
66
|
const apiResult = await tryApiEndpoint(rawText, context);
|
|
67
67
|
if (apiResult)
|
|
68
68
|
return apiResult;
|
|
@@ -76,7 +76,7 @@ export async function llmFallback(rawText, context) {
|
|
|
76
76
|
return null;
|
|
77
77
|
}
|
|
78
78
|
async function tryLLMCli(rawText, context) {
|
|
79
|
-
const cli = process.env.
|
|
79
|
+
const cli = process.env.NOTOKEN_LLM_CLI;
|
|
80
80
|
if (!cli)
|
|
81
81
|
return null;
|
|
82
82
|
try {
|
|
@@ -131,11 +131,11 @@ async function tryLLMCli(rawText, context) {
|
|
|
131
131
|
}
|
|
132
132
|
}
|
|
133
133
|
async function tryApiEndpoint(rawText, context) {
|
|
134
|
-
const endpoint = process.env.
|
|
134
|
+
const endpoint = process.env.NOTOKEN_LLM_ENDPOINT;
|
|
135
135
|
if (!endpoint)
|
|
136
136
|
return null;
|
|
137
|
-
const apiKey = process.env.
|
|
138
|
-
const model = process.env.
|
|
137
|
+
const apiKey = process.env.NOTOKEN_LLM_API_KEY ?? "";
|
|
138
|
+
const model = process.env.NOTOKEN_LLM_MODEL ?? "claude-sonnet-4-20250514";
|
|
139
139
|
const prompt = buildPrompt(rawText, context);
|
|
140
140
|
try {
|
|
141
141
|
const response = await fetch(endpoint, {
|
|
@@ -164,7 +164,7 @@ async function tryApiEndpoint(rawText, context) {
|
|
|
164
164
|
}
|
|
165
165
|
async function tryOllama(rawText, context) {
|
|
166
166
|
const prompt = buildPrompt(rawText, context);
|
|
167
|
-
const model = process.env.
|
|
167
|
+
const model = process.env.NOTOKEN_OLLAMA_MODEL ?? "llama3.2";
|
|
168
168
|
try {
|
|
169
169
|
const response = await fetch("http://localhost:11434/api/generate", {
|
|
170
170
|
method: "POST",
|
package/dist/nlp/llmParser.d.ts
CHANGED
|
@@ -3,6 +3,6 @@ import type { DynamicIntent } from "../types/intent.js";
|
|
|
3
3
|
* LLM-based fallback parser.
|
|
4
4
|
*
|
|
5
5
|
* Sends the raw text + context to an LLM and asks for structured JSON.
|
|
6
|
-
* Set
|
|
6
|
+
* Set NOTOKEN_LLM_ENDPOINT and optionally NOTOKEN_LLM_API_KEY in env.
|
|
7
7
|
*/
|
|
8
8
|
export declare function parseByLLM(rawText: string): Promise<DynamicIntent | null>;
|
package/dist/nlp/llmParser.js
CHANGED
|
@@ -5,13 +5,13 @@ import { loadRules } from "../utils/config.js";
|
|
|
5
5
|
* LLM-based fallback parser.
|
|
6
6
|
*
|
|
7
7
|
* Sends the raw text + context to an LLM and asks for structured JSON.
|
|
8
|
-
* Set
|
|
8
|
+
* Set NOTOKEN_LLM_ENDPOINT and optionally NOTOKEN_LLM_API_KEY in env.
|
|
9
9
|
*/
|
|
10
10
|
export async function parseByLLM(rawText) {
|
|
11
|
-
const endpoint = process.env.
|
|
11
|
+
const endpoint = process.env.NOTOKEN_LLM_ENDPOINT;
|
|
12
12
|
if (!endpoint)
|
|
13
13
|
return null;
|
|
14
|
-
const apiKey = process.env.
|
|
14
|
+
const apiKey = process.env.NOTOKEN_LLM_API_KEY ?? "";
|
|
15
15
|
const rules = loadRules();
|
|
16
16
|
const intents = loadIntents();
|
|
17
17
|
const systemPrompt = buildSystemPrompt(intents, rules);
|
|
@@ -24,7 +24,7 @@ export async function parseByLLM(rawText) {
|
|
|
24
24
|
...(apiKey ? { Authorization: `Bearer ${apiKey}`, "x-api-key": apiKey } : {}),
|
|
25
25
|
},
|
|
26
26
|
body: JSON.stringify({
|
|
27
|
-
model: process.env.
|
|
27
|
+
model: process.env.NOTOKEN_LLM_MODEL ?? "claude-sonnet-4-20250514",
|
|
28
28
|
max_tokens: 512,
|
|
29
29
|
messages: [
|
|
30
30
|
{ role: "system", content: systemPrompt },
|
package/dist/utils/paths.d.ts
CHANGED
|
@@ -1,14 +1,15 @@
|
|
|
1
1
|
/**
|
|
2
2
|
* Centralized path resolution.
|
|
3
3
|
*
|
|
4
|
-
* Single source of truth for all directory paths used by
|
|
5
|
-
*
|
|
6
|
-
* 1. Development (tsx): src/utils/paths.ts → resolve("../..")
|
|
7
|
-
* 2. npm package: dist/utils/paths.js → resolve("../..")
|
|
8
|
-
* 3. SEA binary: embedded assets, writable dirs in ~/.mycli/
|
|
4
|
+
* Single source of truth for all directory paths used by notoken.
|
|
5
|
+
* Everything lives under ~/.notoken/
|
|
9
6
|
*
|
|
10
|
-
*
|
|
11
|
-
*
|
|
7
|
+
* ~/.notoken/
|
|
8
|
+
* data/ — history, sessions
|
|
9
|
+
* logs/ — failures, uncertainty
|
|
10
|
+
* backups/ — auto-backups before file modifications
|
|
11
|
+
* conversations/ — conversation persistence
|
|
12
|
+
* .update-check.json — update cache
|
|
12
13
|
*/
|
|
13
14
|
/** Whether running as a Node.js Single Executable Application */
|
|
14
15
|
export declare function isSEA(): boolean;
|
|
@@ -16,7 +17,7 @@ export declare function isSEA(): boolean;
|
|
|
16
17
|
export declare const PACKAGE_ROOT: string;
|
|
17
18
|
/** Read-only config directory (ships with the package) */
|
|
18
19
|
export declare const CONFIG_DIR: string;
|
|
19
|
-
/** User
|
|
20
|
+
/** User home — everything writable lives here: ~/.notoken/ */
|
|
20
21
|
export declare const USER_HOME: string;
|
|
21
22
|
/** Writable data directory (history, sessions) */
|
|
22
23
|
export declare const DATA_DIR: string;
|
package/dist/utils/paths.js
CHANGED
|
@@ -1,14 +1,15 @@
|
|
|
1
1
|
/**
|
|
2
2
|
* Centralized path resolution.
|
|
3
3
|
*
|
|
4
|
-
* Single source of truth for all directory paths used by
|
|
5
|
-
*
|
|
6
|
-
* 1. Development (tsx): src/utils/paths.ts → resolve("../..")
|
|
7
|
-
* 2. npm package: dist/utils/paths.js → resolve("../..")
|
|
8
|
-
* 3. SEA binary: embedded assets, writable dirs in ~/.mycli/
|
|
4
|
+
* Single source of truth for all directory paths used by notoken.
|
|
5
|
+
* Everything lives under ~/.notoken/
|
|
9
6
|
*
|
|
10
|
-
*
|
|
11
|
-
*
|
|
7
|
+
* ~/.notoken/
|
|
8
|
+
* data/ — history, sessions
|
|
9
|
+
* logs/ — failures, uncertainty
|
|
10
|
+
* backups/ — auto-backups before file modifications
|
|
11
|
+
* conversations/ — conversation persistence
|
|
12
|
+
* .update-check.json — update cache
|
|
12
13
|
*/
|
|
13
14
|
import { resolve, dirname } from "node:path";
|
|
14
15
|
import { fileURLToPath } from "node:url";
|
|
@@ -19,8 +20,6 @@ const __dirname = dirname(__filename);
|
|
|
19
20
|
/** Whether running as a Node.js Single Executable Application */
|
|
20
21
|
export function isSEA() {
|
|
21
22
|
try {
|
|
22
|
-
// node:sea module only exists when running as a SEA binary
|
|
23
|
-
// Use globalThis to check for the injected fuse
|
|
24
23
|
return !!globalThis.__sea_resources__;
|
|
25
24
|
}
|
|
26
25
|
catch {
|
|
@@ -31,8 +30,8 @@ export function isSEA() {
|
|
|
31
30
|
export const PACKAGE_ROOT = resolve(__dirname, "../..");
|
|
32
31
|
/** Read-only config directory (ships with the package) */
|
|
33
32
|
export const CONFIG_DIR = resolve(PACKAGE_ROOT, "config");
|
|
34
|
-
/** User
|
|
35
|
-
export const USER_HOME = resolve(process.env.
|
|
33
|
+
/** User home — everything writable lives here: ~/.notoken/ */
|
|
34
|
+
export const USER_HOME = resolve(process.env.NOTOKEN_HOME ?? resolve(homedir(), ".notoken"));
|
|
36
35
|
/** Writable data directory (history, sessions) */
|
|
37
36
|
export const DATA_DIR = resolve(USER_HOME, "data");
|
|
38
37
|
/** Writable logs directory (failures, uncertainty) */
|
|
@@ -0,0 +1,32 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Update checker.
|
|
3
|
+
*
|
|
4
|
+
* Checks notoken.sh/api/version (primary) or npm registry (fallback)
|
|
5
|
+
* for the latest version. Caches result for 1 hour.
|
|
6
|
+
*
|
|
7
|
+
* Used by both CLI (startup banner) and desktop app (badge).
|
|
8
|
+
*/
|
|
9
|
+
export interface UpdateInfo {
|
|
10
|
+
current: string;
|
|
11
|
+
latest: string;
|
|
12
|
+
updateAvailable: boolean;
|
|
13
|
+
checkedAt: string;
|
|
14
|
+
}
|
|
15
|
+
/**
|
|
16
|
+
* Check for updates. Returns cached result if fresh.
|
|
17
|
+
* Non-blocking — never throws, returns null on failure.
|
|
18
|
+
*/
|
|
19
|
+
export declare function checkForUpdate(): Promise<UpdateInfo | null>;
|
|
20
|
+
/**
|
|
21
|
+
* Synchronous check — reads cache only, no network.
|
|
22
|
+
* Use this for startup banner (non-blocking).
|
|
23
|
+
*/
|
|
24
|
+
export declare function checkForUpdateSync(): UpdateInfo | null;
|
|
25
|
+
/**
|
|
26
|
+
* Run the actual update.
|
|
27
|
+
*/
|
|
28
|
+
export declare function runUpdate(): string;
|
|
29
|
+
/**
|
|
30
|
+
* Format update banner for terminal.
|
|
31
|
+
*/
|
|
32
|
+
export declare function formatUpdateBanner(info: UpdateInfo): string;
|
|
@@ -0,0 +1,157 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Update checker.
|
|
3
|
+
*
|
|
4
|
+
* Checks notoken.sh/api/version (primary) or npm registry (fallback)
|
|
5
|
+
* for the latest version. Caches result for 1 hour.
|
|
6
|
+
*
|
|
7
|
+
* Used by both CLI (startup banner) and desktop app (badge).
|
|
8
|
+
*/
|
|
9
|
+
import { existsSync, readFileSync, writeFileSync, mkdirSync } from "node:fs";
|
|
10
|
+
import { resolve } from "node:path";
|
|
11
|
+
import { execSync } from "node:child_process";
|
|
12
|
+
import { USER_HOME } from "./paths.js";
|
|
13
|
+
const CACHE_FILE = resolve(USER_HOME, ".update-check.json");
|
|
14
|
+
const CACHE_TTL = 3600_000; // 1 hour
|
|
15
|
+
const CURRENT_VERSION = getInstalledVersion();
|
|
16
|
+
/**
|
|
17
|
+
* Check for updates. Returns cached result if fresh.
|
|
18
|
+
* Non-blocking — never throws, returns null on failure.
|
|
19
|
+
*/
|
|
20
|
+
export async function checkForUpdate() {
|
|
21
|
+
try {
|
|
22
|
+
// Check cache first
|
|
23
|
+
const cached = readCache();
|
|
24
|
+
if (cached)
|
|
25
|
+
return cached;
|
|
26
|
+
// Fetch latest version
|
|
27
|
+
const latest = await fetchLatestVersion();
|
|
28
|
+
if (!latest)
|
|
29
|
+
return null;
|
|
30
|
+
const info = {
|
|
31
|
+
current: CURRENT_VERSION,
|
|
32
|
+
latest,
|
|
33
|
+
updateAvailable: isNewer(latest, CURRENT_VERSION),
|
|
34
|
+
checkedAt: new Date().toISOString(),
|
|
35
|
+
};
|
|
36
|
+
writeCache(info);
|
|
37
|
+
return info;
|
|
38
|
+
}
|
|
39
|
+
catch {
|
|
40
|
+
return null;
|
|
41
|
+
}
|
|
42
|
+
}
|
|
43
|
+
/**
|
|
44
|
+
* Synchronous check — reads cache only, no network.
|
|
45
|
+
* Use this for startup banner (non-blocking).
|
|
46
|
+
*/
|
|
47
|
+
export function checkForUpdateSync() {
|
|
48
|
+
return readCache();
|
|
49
|
+
}
|
|
50
|
+
/**
|
|
51
|
+
* Run the actual update.
|
|
52
|
+
*/
|
|
53
|
+
export function runUpdate() {
|
|
54
|
+
try {
|
|
55
|
+
const result = execSync("npm install -g notoken@latest", {
|
|
56
|
+
encoding: "utf-8",
|
|
57
|
+
timeout: 120_000,
|
|
58
|
+
stdio: ["pipe", "pipe", "pipe"],
|
|
59
|
+
});
|
|
60
|
+
// Clear cache so next check picks up new version
|
|
61
|
+
clearCache();
|
|
62
|
+
return result;
|
|
63
|
+
}
|
|
64
|
+
catch (err) {
|
|
65
|
+
throw new Error(`Update failed: ${err instanceof Error ? err.message : err}`);
|
|
66
|
+
}
|
|
67
|
+
}
|
|
68
|
+
/**
|
|
69
|
+
* Format update banner for terminal.
|
|
70
|
+
*/
|
|
71
|
+
export function formatUpdateBanner(info) {
|
|
72
|
+
if (!info.updateAvailable)
|
|
73
|
+
return "";
|
|
74
|
+
return `\x1b[33m⬆ Update available: ${info.current} → ${info.latest}\x1b[0m \x1b[2m(notoken update)\x1b[0m`;
|
|
75
|
+
}
|
|
76
|
+
// ─── Internals ──────────────────────────────────────────────────────────────
|
|
77
|
+
async function fetchLatestVersion() {
|
|
78
|
+
// Try notoken.sh API first
|
|
79
|
+
try {
|
|
80
|
+
const response = await fetch("https://notoken.sh/api/version", { signal: AbortSignal.timeout(5000) });
|
|
81
|
+
if (response.ok) {
|
|
82
|
+
const data = (await response.json());
|
|
83
|
+
if (data.version)
|
|
84
|
+
return data.version;
|
|
85
|
+
}
|
|
86
|
+
}
|
|
87
|
+
catch { }
|
|
88
|
+
// Fallback: npm registry
|
|
89
|
+
try {
|
|
90
|
+
const response = await fetch("https://registry.npmjs.org/notoken/latest", { signal: AbortSignal.timeout(5000) });
|
|
91
|
+
if (response.ok) {
|
|
92
|
+
const data = (await response.json());
|
|
93
|
+
if (data.version)
|
|
94
|
+
return data.version;
|
|
95
|
+
}
|
|
96
|
+
}
|
|
97
|
+
catch { }
|
|
98
|
+
// Fallback: npm CLI
|
|
99
|
+
try {
|
|
100
|
+
const result = execSync("npm view notoken version", { encoding: "utf-8", timeout: 10_000, stdio: ["pipe", "pipe", "pipe"] });
|
|
101
|
+
return result.trim() || null;
|
|
102
|
+
}
|
|
103
|
+
catch { }
|
|
104
|
+
return null;
|
|
105
|
+
}
|
|
106
|
+
function getInstalledVersion() {
|
|
107
|
+
try {
|
|
108
|
+
// Read from our own package.json
|
|
109
|
+
const pkg = JSON.parse(execSync("npm list -g notoken --json --depth=0 2>/dev/null", { encoding: "utf-8", timeout: 5000 }));
|
|
110
|
+
return pkg.dependencies?.notoken?.version ?? "0.0.0";
|
|
111
|
+
}
|
|
112
|
+
catch {
|
|
113
|
+
return "0.0.0";
|
|
114
|
+
}
|
|
115
|
+
}
|
|
116
|
+
function isNewer(latest, current) {
|
|
117
|
+
const l = latest.split(".").map(Number);
|
|
118
|
+
const c = current.split(".").map(Number);
|
|
119
|
+
for (let i = 0; i < 3; i++) {
|
|
120
|
+
if ((l[i] ?? 0) > (c[i] ?? 0))
|
|
121
|
+
return true;
|
|
122
|
+
if ((l[i] ?? 0) < (c[i] ?? 0))
|
|
123
|
+
return false;
|
|
124
|
+
}
|
|
125
|
+
return false;
|
|
126
|
+
}
|
|
127
|
+
function readCache() {
|
|
128
|
+
try {
|
|
129
|
+
if (!existsSync(CACHE_FILE))
|
|
130
|
+
return null;
|
|
131
|
+
const raw = JSON.parse(readFileSync(CACHE_FILE, "utf-8"));
|
|
132
|
+
const age = Date.now() - new Date(raw.checkedAt).getTime();
|
|
133
|
+
if (age > CACHE_TTL)
|
|
134
|
+
return null;
|
|
135
|
+
// Refresh current version in case user updated
|
|
136
|
+
raw.current = CURRENT_VERSION;
|
|
137
|
+
raw.updateAvailable = isNewer(raw.latest, CURRENT_VERSION);
|
|
138
|
+
return raw;
|
|
139
|
+
}
|
|
140
|
+
catch {
|
|
141
|
+
return null;
|
|
142
|
+
}
|
|
143
|
+
}
|
|
144
|
+
function writeCache(info) {
|
|
145
|
+
try {
|
|
146
|
+
mkdirSync(USER_HOME, { recursive: true });
|
|
147
|
+
writeFileSync(CACHE_FILE, JSON.stringify(info));
|
|
148
|
+
}
|
|
149
|
+
catch { }
|
|
150
|
+
}
|
|
151
|
+
function clearCache() {
|
|
152
|
+
try {
|
|
153
|
+
if (existsSync(CACHE_FILE))
|
|
154
|
+
writeFileSync(CACHE_FILE, "{}");
|
|
155
|
+
}
|
|
156
|
+
catch { }
|
|
157
|
+
}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "notoken-core",
|
|
3
|
-
"version": "1.
|
|
3
|
+
"version": "1.2.0",
|
|
4
4
|
"description": "Shared engine for notoken — NLP parsing, execution, detection, analysis",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"license": "MIT",
|
|
@@ -16,7 +16,10 @@
|
|
|
16
16
|
"exports": {
|
|
17
17
|
".": "./dist/index.js"
|
|
18
18
|
},
|
|
19
|
-
"files": [
|
|
19
|
+
"files": [
|
|
20
|
+
"dist/",
|
|
21
|
+
"config/"
|
|
22
|
+
],
|
|
20
23
|
"scripts": {
|
|
21
24
|
"build": "tsc",
|
|
22
25
|
"test": "vitest run"
|