@takuhon/cli 0.13.0 → 0.14.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
CHANGED
|
@@ -2,10 +2,10 @@
|
|
|
2
2
|
import {
|
|
3
3
|
ADMIN_DIST_DIRNAME,
|
|
4
4
|
resolveAdminBundleDir
|
|
5
|
-
} from "./chunk-
|
|
5
|
+
} from "./chunk-YLSMA3YM.js";
|
|
6
6
|
|
|
7
7
|
// src/index.ts
|
|
8
|
-
import { readFileSync as
|
|
8
|
+
import { readFileSync as readFileSync16, realpathSync } from "fs";
|
|
9
9
|
import { stdin, stdout } from "process";
|
|
10
10
|
import { createInterface } from "readline/promises";
|
|
11
11
|
import { fileURLToPath } from "url";
|
|
@@ -2147,18 +2147,144 @@ function isNotFound5(error) {
|
|
|
2147
2147
|
return typeof error === "object" && error !== null && error.code === "ENOENT";
|
|
2148
2148
|
}
|
|
2149
2149
|
|
|
2150
|
-
// src/
|
|
2150
|
+
// src/mcp-command.ts
|
|
2151
2151
|
import { readFileSync as readFileSync10 } from "fs";
|
|
2152
|
+
import { StdioServerTransport } from "@modelcontextprotocol/sdk/server/stdio.js";
|
|
2153
|
+
import { validate as validate4 } from "@takuhon/core";
|
|
2154
|
+
import { createTakuhonMcpServer } from "@takuhon/mcp";
|
|
2155
|
+
var DEFAULT_PATH7 = "takuhon.json";
|
|
2156
|
+
var SERVER_NAME = "takuhon";
|
|
2157
|
+
var USAGE6 = `Usage: takuhon mcp [path]
|
|
2158
|
+
|
|
2159
|
+
Serve a takuhon.json over the Model Context Protocol on stdio, so an MCP client
|
|
2160
|
+
(e.g. Claude Desktop) can read the profile read-only. With no path, serves
|
|
2161
|
+
./takuhon.json. The file is re-read on every request, so edits are reflected
|
|
2162
|
+
without restarting.
|
|
2163
|
+
|
|
2164
|
+
Exposes (read-only, the same surface as the public API, privacy filter applied):
|
|
2165
|
+
tools: get_profile, get_section, get_jsonld, list_locales
|
|
2166
|
+
resources: takuhon://profile, takuhon://schema
|
|
2167
|
+
|
|
2168
|
+
stdin/stdout carry the protocol stream; diagnostics go to stderr. The server
|
|
2169
|
+
runs until the client disconnects or you press Ctrl-C.
|
|
2170
|
+
|
|
2171
|
+
Example Claude Desktop config (claude_desktop_config.json):
|
|
2172
|
+
{
|
|
2173
|
+
"mcpServers": {
|
|
2174
|
+
"my-profile": { "command": "takuhon", "args": ["mcp", "/path/to/takuhon.json"] }
|
|
2175
|
+
}
|
|
2176
|
+
}
|
|
2177
|
+
|
|
2178
|
+
Exit codes: 0 = served then stopped, 2 = bad arguments / file missing /
|
|
2179
|
+
unreadable / invalid.
|
|
2180
|
+
`;
|
|
2181
|
+
var VERSION = readCliVersion();
|
|
2182
|
+
function loadMcpProfile(path) {
|
|
2183
|
+
let raw;
|
|
2184
|
+
try {
|
|
2185
|
+
raw = readFileSync10(path, "utf8");
|
|
2186
|
+
} catch {
|
|
2187
|
+
throw new Error(`cannot read '${path}'.`);
|
|
2188
|
+
}
|
|
2189
|
+
let data;
|
|
2190
|
+
try {
|
|
2191
|
+
data = JSON.parse(raw);
|
|
2192
|
+
} catch (error) {
|
|
2193
|
+
const detail = error instanceof Error ? error.message : String(error);
|
|
2194
|
+
throw new Error(`'${path}' is not valid JSON: ${detail}`, { cause: error });
|
|
2195
|
+
}
|
|
2196
|
+
const result = validate4(data);
|
|
2197
|
+
if (!result.ok) {
|
|
2198
|
+
const lines = result.errors.map((e) => ` ${e.pointer || "/"}: ${e.message}`);
|
|
2199
|
+
throw new Error(`'${path}' is not a valid takuhon profile:
|
|
2200
|
+
${lines.join("\n")}`);
|
|
2201
|
+
}
|
|
2202
|
+
return result.data;
|
|
2203
|
+
}
|
|
2204
|
+
async function runMcp(args = [], deps = {}) {
|
|
2205
|
+
const err = deps.stderr ?? ((text) => void process.stderr.write(text));
|
|
2206
|
+
if (args[0] === "--help" || args[0] === "-h") {
|
|
2207
|
+
const out = deps.stdout ?? ((text) => void process.stdout.write(text));
|
|
2208
|
+
out(USAGE6);
|
|
2209
|
+
return 0;
|
|
2210
|
+
}
|
|
2211
|
+
const parsed = parseArgs6(args);
|
|
2212
|
+
if ("error" in parsed) {
|
|
2213
|
+
err(`${parsed.error}
|
|
2214
|
+
Run \`takuhon mcp --help\` for usage.
|
|
2215
|
+
`);
|
|
2216
|
+
return 2;
|
|
2217
|
+
}
|
|
2218
|
+
try {
|
|
2219
|
+
loadMcpProfile(parsed.path);
|
|
2220
|
+
} catch (error) {
|
|
2221
|
+
err(`takuhon: ${error instanceof Error ? error.message : String(error)}
|
|
2222
|
+
`);
|
|
2223
|
+
return 2;
|
|
2224
|
+
}
|
|
2225
|
+
const server = createTakuhonMcpServer({
|
|
2226
|
+
// Re-read per request so edits are reflected; if the file later becomes
|
|
2227
|
+
// invalid, the load failure surfaces to the client as a tool error result.
|
|
2228
|
+
loadProfile: () => loadMcpProfile(parsed.path),
|
|
2229
|
+
name: SERVER_NAME,
|
|
2230
|
+
version: VERSION
|
|
2231
|
+
});
|
|
2232
|
+
const transport = (deps.createTransport ?? (() => new StdioServerTransport()))();
|
|
2233
|
+
await server.connect(transport);
|
|
2234
|
+
err(`takuhon mcp: serving ${parsed.path} (read-only) on stdio
|
|
2235
|
+
`);
|
|
2236
|
+
return await new Promise((resolve7) => {
|
|
2237
|
+
let done = false;
|
|
2238
|
+
function finish() {
|
|
2239
|
+
if (done) return;
|
|
2240
|
+
done = true;
|
|
2241
|
+
process.removeListener("SIGINT", shutdown);
|
|
2242
|
+
process.removeListener("SIGTERM", shutdown);
|
|
2243
|
+
resolve7(0);
|
|
2244
|
+
}
|
|
2245
|
+
function shutdown() {
|
|
2246
|
+
void server.close();
|
|
2247
|
+
finish();
|
|
2248
|
+
}
|
|
2249
|
+
server.server.onclose = finish;
|
|
2250
|
+
process.once("SIGINT", shutdown);
|
|
2251
|
+
process.once("SIGTERM", shutdown);
|
|
2252
|
+
});
|
|
2253
|
+
}
|
|
2254
|
+
function parseArgs6(args) {
|
|
2255
|
+
let path;
|
|
2256
|
+
for (const arg of args) {
|
|
2257
|
+
if (arg.startsWith("-")) {
|
|
2258
|
+
return { error: `takuhon: unknown option \`${arg}\` for \`mcp\`.` };
|
|
2259
|
+
}
|
|
2260
|
+
if (path !== void 0) {
|
|
2261
|
+
return { error: "takuhon: `mcp` takes at most one path argument." };
|
|
2262
|
+
}
|
|
2263
|
+
path = arg;
|
|
2264
|
+
}
|
|
2265
|
+
return { path: path ?? DEFAULT_PATH7 };
|
|
2266
|
+
}
|
|
2267
|
+
function readCliVersion() {
|
|
2268
|
+
try {
|
|
2269
|
+
const pkg2 = JSON.parse(readFileSync10(new URL("../package.json", import.meta.url), "utf8"));
|
|
2270
|
+
return pkg2.version;
|
|
2271
|
+
} catch {
|
|
2272
|
+
return "0.0.0";
|
|
2273
|
+
}
|
|
2274
|
+
}
|
|
2275
|
+
|
|
2276
|
+
// src/migrate-command.ts
|
|
2277
|
+
import { readFileSync as readFileSync11 } from "fs";
|
|
2152
2278
|
import { resolve as resolve5 } from "path";
|
|
2153
2279
|
import {
|
|
2154
2280
|
MigrationError as MigrationError2,
|
|
2155
2281
|
SCHEMA_VERSION as SCHEMA_VERSION2,
|
|
2156
2282
|
SUPPORTED_SCHEMA_VERSIONS,
|
|
2157
2283
|
migrateTakuhon as migrateTakuhon2,
|
|
2158
|
-
validate as
|
|
2284
|
+
validate as validate5
|
|
2159
2285
|
} from "@takuhon/core";
|
|
2160
|
-
var
|
|
2161
|
-
var
|
|
2286
|
+
var DEFAULT_PATH8 = "takuhon.json";
|
|
2287
|
+
var USAGE7 = `Usage: takuhon migrate [path] [--to <version>] [--out <file>] [--dry-run]
|
|
2162
2288
|
|
|
2163
2289
|
Forward-migrate a takuhon.json to a newer schema version. The source version
|
|
2164
2290
|
is read from the file's own schemaVersion. With no path, migrates
|
|
@@ -2180,9 +2306,9 @@ Exit codes: 0 = migrated / already current / dry-run, 1 = cannot migrate,
|
|
|
2180
2306
|
`;
|
|
2181
2307
|
function runMigrate(args = [], deps = {}) {
|
|
2182
2308
|
if (args[0] === "--help" || args[0] === "-h") {
|
|
2183
|
-
return { code: 0, stdout:
|
|
2309
|
+
return { code: 0, stdout: USAGE7, stderr: "" };
|
|
2184
2310
|
}
|
|
2185
|
-
const parsed =
|
|
2311
|
+
const parsed = parseArgs7(args);
|
|
2186
2312
|
if ("error" in parsed) {
|
|
2187
2313
|
return {
|
|
2188
2314
|
code: 2,
|
|
@@ -2195,7 +2321,7 @@ Run \`takuhon migrate --help\` for usage.
|
|
|
2195
2321
|
const now = deps.now ?? (() => /* @__PURE__ */ new Date());
|
|
2196
2322
|
return migrateFile(parsed, now);
|
|
2197
2323
|
}
|
|
2198
|
-
function
|
|
2324
|
+
function parseArgs7(args) {
|
|
2199
2325
|
let path;
|
|
2200
2326
|
let to;
|
|
2201
2327
|
let out;
|
|
@@ -2238,13 +2364,13 @@ function parseArgs6(args) {
|
|
|
2238
2364
|
error: `takuhon: unsupported --to version "${target}". Supported: ${SUPPORTED_SCHEMA_VERSIONS.join(", ")}.`
|
|
2239
2365
|
};
|
|
2240
2366
|
}
|
|
2241
|
-
return { path: path ??
|
|
2367
|
+
return { path: path ?? DEFAULT_PATH8, to: target, out, dryRun };
|
|
2242
2368
|
}
|
|
2243
2369
|
function migrateFile(parsed, now) {
|
|
2244
2370
|
const { path, to: target, out, dryRun } = parsed;
|
|
2245
2371
|
let raw;
|
|
2246
2372
|
try {
|
|
2247
|
-
raw =
|
|
2373
|
+
raw = readFileSync11(path, "utf8");
|
|
2248
2374
|
} catch {
|
|
2249
2375
|
return {
|
|
2250
2376
|
code: 2,
|
|
@@ -2295,7 +2421,7 @@ Migrations are forward-only; to move to an older schema, restore from a backup w
|
|
|
2295
2421
|
}
|
|
2296
2422
|
throw error;
|
|
2297
2423
|
}
|
|
2298
|
-
const revalidated =
|
|
2424
|
+
const revalidated = validate5(migrated);
|
|
2299
2425
|
if (!revalidated.ok) {
|
|
2300
2426
|
const lines2 = revalidated.errors.map((e) => ` ${e.pointer || "/"}: ${e.message}`);
|
|
2301
2427
|
return {
|
|
@@ -2358,11 +2484,11 @@ ${lines2.join("\n")}
|
|
|
2358
2484
|
}
|
|
2359
2485
|
|
|
2360
2486
|
// src/refresh-admin-command.ts
|
|
2361
|
-
import { readFileSync as
|
|
2487
|
+
import { readFileSync as readFileSync12 } from "fs";
|
|
2362
2488
|
import { cp, mkdtemp, readdir, rename, rm, stat } from "fs/promises";
|
|
2363
2489
|
import { join as join7, resolve as resolve6 } from "path";
|
|
2364
2490
|
var PROFILE_FILENAME = "takuhon.json";
|
|
2365
|
-
var
|
|
2491
|
+
var USAGE8 = `Usage: takuhon admin update [path]
|
|
2366
2492
|
|
|
2367
2493
|
Refresh a project's ${ADMIN_DIST_DIRNAME}/ with the admin form-UI bundle shipped in
|
|
2368
2494
|
this @takuhon/cli. Use it after upgrading @takuhon/cli to pick up a newer admin
|
|
@@ -2375,7 +2501,7 @@ no ${ADMIN_DIST_DIRNAME}/ to refresh / copy failed.
|
|
|
2375
2501
|
`;
|
|
2376
2502
|
async function runRefreshAdmin(args = [], opts = {}) {
|
|
2377
2503
|
if (args[0] === "--help" || args[0] === "-h") {
|
|
2378
|
-
return { code: 0, stdout:
|
|
2504
|
+
return { code: 0, stdout: USAGE8, stderr: "" };
|
|
2379
2505
|
}
|
|
2380
2506
|
const unknownFlag = args.find((arg) => arg.startsWith("-"));
|
|
2381
2507
|
if (unknownFlag !== void 0) {
|
|
@@ -2458,7 +2584,7 @@ Run \`takuhon admin update --help\` for usage.
|
|
|
2458
2584
|
};
|
|
2459
2585
|
}
|
|
2460
2586
|
function readVersion() {
|
|
2461
|
-
const pkg2 = JSON.parse(
|
|
2587
|
+
const pkg2 = JSON.parse(readFileSync12(new URL("../package.json", import.meta.url), "utf8"));
|
|
2462
2588
|
return pkg2.version;
|
|
2463
2589
|
}
|
|
2464
2590
|
function isNodeErrnoException(err) {
|
|
@@ -2466,10 +2592,10 @@ function isNodeErrnoException(err) {
|
|
|
2466
2592
|
}
|
|
2467
2593
|
|
|
2468
2594
|
// src/restore-command.ts
|
|
2469
|
-
import { readFileSync as
|
|
2470
|
-
import { validate as
|
|
2471
|
-
var
|
|
2472
|
-
var
|
|
2595
|
+
import { readFileSync as readFileSync13 } from "fs";
|
|
2596
|
+
import { validate as validate6 } from "@takuhon/core";
|
|
2597
|
+
var DEFAULT_PATH9 = "takuhon.json";
|
|
2598
|
+
var USAGE9 = `Usage: takuhon restore --from <backup> [path] [--yes]
|
|
2473
2599
|
|
|
2474
2600
|
Overwrite a profile with a previously saved backup. With no path, restores
|
|
2475
2601
|
./takuhon.json in the current working directory.
|
|
@@ -2486,9 +2612,9 @@ Exit codes: 0 = restored / aborted, 1 = backup failed validation,
|
|
|
2486
2612
|
`;
|
|
2487
2613
|
async function runRestore(args = [], deps = {}) {
|
|
2488
2614
|
if (args[0] === "--help" || args[0] === "-h") {
|
|
2489
|
-
return { code: 0, stdout:
|
|
2615
|
+
return { code: 0, stdout: USAGE9, stderr: "" };
|
|
2490
2616
|
}
|
|
2491
|
-
const parsed =
|
|
2617
|
+
const parsed = parseArgs8(args);
|
|
2492
2618
|
if ("error" in parsed) {
|
|
2493
2619
|
return {
|
|
2494
2620
|
code: 2,
|
|
@@ -2501,7 +2627,7 @@ Run \`takuhon restore --help\` for usage.
|
|
|
2501
2627
|
const now = deps.now ?? (() => /* @__PURE__ */ new Date());
|
|
2502
2628
|
return restoreFile(parsed, now, deps.confirm);
|
|
2503
2629
|
}
|
|
2504
|
-
function
|
|
2630
|
+
function parseArgs8(args) {
|
|
2505
2631
|
let from;
|
|
2506
2632
|
let path;
|
|
2507
2633
|
let yes = false;
|
|
@@ -2535,13 +2661,13 @@ function parseArgs7(args) {
|
|
|
2535
2661
|
if (from === void 0 || from.length === 0) {
|
|
2536
2662
|
return { error: "takuhon: `restore` requires `--from <backup>`." };
|
|
2537
2663
|
}
|
|
2538
|
-
return { from, path: path ??
|
|
2664
|
+
return { from, path: path ?? DEFAULT_PATH9, yes };
|
|
2539
2665
|
}
|
|
2540
2666
|
async function restoreFile(parsed, now, confirm) {
|
|
2541
2667
|
const { from, path, yes } = parsed;
|
|
2542
2668
|
let backupRaw;
|
|
2543
2669
|
try {
|
|
2544
|
-
backupRaw =
|
|
2670
|
+
backupRaw = readFileSync13(from, "utf8");
|
|
2545
2671
|
} catch {
|
|
2546
2672
|
return { code: 2, stdout: "", stderr: `takuhon: cannot read backup '${from}'.
|
|
2547
2673
|
` };
|
|
@@ -2558,7 +2684,7 @@ async function restoreFile(parsed, now, confirm) {
|
|
|
2558
2684
|
`
|
|
2559
2685
|
};
|
|
2560
2686
|
}
|
|
2561
|
-
const result =
|
|
2687
|
+
const result = validate6(backupData);
|
|
2562
2688
|
if (!result.ok) {
|
|
2563
2689
|
const lines2 = result.errors.map((e) => ` ${e.pointer || "/"}: ${e.message}`);
|
|
2564
2690
|
return {
|
|
@@ -2571,7 +2697,7 @@ ${lines2.join("\n")}
|
|
|
2571
2697
|
}
|
|
2572
2698
|
let currentRaw;
|
|
2573
2699
|
try {
|
|
2574
|
-
currentRaw =
|
|
2700
|
+
currentRaw = readFileSync13(path, "utf8");
|
|
2575
2701
|
} catch (error) {
|
|
2576
2702
|
if (!isNotFound6(error)) {
|
|
2577
2703
|
return { code: 2, stdout: "", stderr: `takuhon: cannot read current profile '${path}'.
|
|
@@ -2641,12 +2767,12 @@ function isNotFound6(error) {
|
|
|
2641
2767
|
}
|
|
2642
2768
|
|
|
2643
2769
|
// src/sync-command.ts
|
|
2644
|
-
import { readFileSync as
|
|
2645
|
-
import { validate as
|
|
2646
|
-
var
|
|
2770
|
+
import { readFileSync as readFileSync14 } from "fs";
|
|
2771
|
+
import { validate as validate7 } from "@takuhon/core";
|
|
2772
|
+
var DEFAULT_PATH10 = "takuhon.json";
|
|
2647
2773
|
var ADMIN_PROFILE_PATH = "/api/admin/profile";
|
|
2648
2774
|
var TOKEN_ENV = "TAKUHON_ADMIN_TOKEN";
|
|
2649
|
-
var
|
|
2775
|
+
var USAGE10 = `Usage: takuhon sync [path] --url <base-url> [--if-match <etag>] [--dry-run]
|
|
2650
2776
|
|
|
2651
2777
|
Push a local takuhon.json to a deployed takuhon instance by calling its admin
|
|
2652
2778
|
write endpoint (PUT <base-url>/api/admin/profile). With no path, syncs
|
|
@@ -2672,9 +2798,9 @@ unset / auth failure / network error / other non-success response.
|
|
|
2672
2798
|
`;
|
|
2673
2799
|
async function runSync(args = [], deps = {}) {
|
|
2674
2800
|
if (args[0] === "--help" || args[0] === "-h") {
|
|
2675
|
-
return { code: 0, stdout:
|
|
2801
|
+
return { code: 0, stdout: USAGE10, stderr: "" };
|
|
2676
2802
|
}
|
|
2677
|
-
const parsed =
|
|
2803
|
+
const parsed = parseArgs9(args);
|
|
2678
2804
|
if ("error" in parsed) {
|
|
2679
2805
|
return {
|
|
2680
2806
|
code: 2,
|
|
@@ -2686,7 +2812,7 @@ Run \`takuhon sync --help\` for usage.
|
|
|
2686
2812
|
}
|
|
2687
2813
|
return syncProfile(parsed, deps);
|
|
2688
2814
|
}
|
|
2689
|
-
function
|
|
2815
|
+
function parseArgs9(args) {
|
|
2690
2816
|
let path;
|
|
2691
2817
|
let url;
|
|
2692
2818
|
let ifMatch;
|
|
@@ -2732,7 +2858,7 @@ function parseArgs8(args) {
|
|
|
2732
2858
|
}
|
|
2733
2859
|
const base = parseOrigin(url);
|
|
2734
2860
|
if ("error" in base) return base;
|
|
2735
|
-
return { path: path ??
|
|
2861
|
+
return { path: path ?? DEFAULT_PATH10, url: base.origin, ifMatch, dryRun };
|
|
2736
2862
|
}
|
|
2737
2863
|
function parseOrigin(value) {
|
|
2738
2864
|
let parsed;
|
|
@@ -2765,7 +2891,7 @@ async function syncProfile(parsed, deps) {
|
|
|
2765
2891
|
const { path, url, ifMatch, dryRun } = parsed;
|
|
2766
2892
|
let raw;
|
|
2767
2893
|
try {
|
|
2768
|
-
raw =
|
|
2894
|
+
raw = readFileSync14(path, "utf8");
|
|
2769
2895
|
} catch {
|
|
2770
2896
|
return { code: 2, stdout: "", stderr: `takuhon: cannot read '${path}'.
|
|
2771
2897
|
` };
|
|
@@ -2778,7 +2904,7 @@ async function syncProfile(parsed, deps) {
|
|
|
2778
2904
|
return { code: 2, stdout: "", stderr: `takuhon: '${path}' is not valid JSON: ${detail}
|
|
2779
2905
|
` };
|
|
2780
2906
|
}
|
|
2781
|
-
const result =
|
|
2907
|
+
const result = validate7(data);
|
|
2782
2908
|
if (!result.ok) {
|
|
2783
2909
|
const lines = result.errors.map((e) => ` ${e.pointer || "/"}: ${e.message}`);
|
|
2784
2910
|
return {
|
|
@@ -2902,10 +3028,10 @@ async function readProblem(res) {
|
|
|
2902
3028
|
}
|
|
2903
3029
|
|
|
2904
3030
|
// src/validate-command.ts
|
|
2905
|
-
import { readFileSync as
|
|
2906
|
-
import { validate as
|
|
2907
|
-
var
|
|
2908
|
-
var
|
|
3031
|
+
import { readFileSync as readFileSync15 } from "fs";
|
|
3032
|
+
import { validate as validate8 } from "@takuhon/core";
|
|
3033
|
+
var DEFAULT_PATH11 = "takuhon.json";
|
|
3034
|
+
var USAGE11 = `Usage: takuhon validate [path]
|
|
2909
3035
|
|
|
2910
3036
|
Validate a takuhon.json against the takuhon schema. With no path, validates
|
|
2911
3037
|
./takuhon.json in the current working directory.
|
|
@@ -2914,7 +3040,7 @@ Exit codes: 0 = valid, 1 = invalid, 2 = file missing / unreadable / not JSON.
|
|
|
2914
3040
|
`;
|
|
2915
3041
|
function runValidate(args = []) {
|
|
2916
3042
|
if (args[0] === "--help" || args[0] === "-h") {
|
|
2917
|
-
return { code: 0, stdout:
|
|
3043
|
+
return { code: 0, stdout: USAGE11, stderr: "" };
|
|
2918
3044
|
}
|
|
2919
3045
|
if (args.length > 1) {
|
|
2920
3046
|
return {
|
|
@@ -2926,10 +3052,10 @@ function runValidate(args = []) {
|
|
|
2926
3052
|
return validateFile(args[0]);
|
|
2927
3053
|
}
|
|
2928
3054
|
function validateFile(pathArg) {
|
|
2929
|
-
const target = pathArg ??
|
|
3055
|
+
const target = pathArg ?? DEFAULT_PATH11;
|
|
2930
3056
|
let raw;
|
|
2931
3057
|
try {
|
|
2932
|
-
raw =
|
|
3058
|
+
raw = readFileSync15(target, "utf8");
|
|
2933
3059
|
} catch {
|
|
2934
3060
|
return {
|
|
2935
3061
|
code: 2,
|
|
@@ -2950,7 +3076,7 @@ function validateFile(pathArg) {
|
|
|
2950
3076
|
`
|
|
2951
3077
|
};
|
|
2952
3078
|
}
|
|
2953
|
-
const result =
|
|
3079
|
+
const result = validate8(data);
|
|
2954
3080
|
if (result.ok) {
|
|
2955
3081
|
return {
|
|
2956
3082
|
code: 0,
|
|
@@ -2971,9 +3097,9 @@ ${lines.join("\n")}
|
|
|
2971
3097
|
}
|
|
2972
3098
|
|
|
2973
3099
|
// src/index.ts
|
|
2974
|
-
var pkg = JSON.parse(
|
|
2975
|
-
var
|
|
2976
|
-
var HELP = `takuhon ${
|
|
3100
|
+
var pkg = JSON.parse(readFileSync16(new URL("../package.json", import.meta.url), "utf8"));
|
|
3101
|
+
var VERSION2 = pkg.version;
|
|
3102
|
+
var HELP = `takuhon ${VERSION2}
|
|
2977
3103
|
|
|
2978
3104
|
Takuhon \u2014 open-source portable profile API server.
|
|
2979
3105
|
|
|
@@ -3004,6 +3130,9 @@ Commands:
|
|
|
3004
3130
|
takuhon admin update [path] Refresh a project's admin-dist/ with the admin form UI
|
|
3005
3131
|
bundled in this @takuhon/cli (use after upgrading the
|
|
3006
3132
|
CLI). Updates an existing admin-dist/ only.
|
|
3133
|
+
takuhon mcp [path] Serve a takuhon.json over the Model Context Protocol on
|
|
3134
|
+
stdio (read-only), so an MCP client such as Claude Desktop
|
|
3135
|
+
can read the profile. Re-reads the file per request.
|
|
3007
3136
|
takuhon sync [path] --url <url> Push a takuhon.json to a deployment's admin API
|
|
3008
3137
|
(PUT <url>/api/admin/profile). Reads the admin token
|
|
3009
3138
|
from TAKUHON_ADMIN_TOKEN. --if-match <etag> opts into
|
|
@@ -3021,7 +3150,7 @@ Scaffolding a new profile project:
|
|
|
3021
3150
|
async function main(argv) {
|
|
3022
3151
|
const first = argv[0];
|
|
3023
3152
|
if (first === "--version" || first === "-v") {
|
|
3024
|
-
process.stdout.write(`${
|
|
3153
|
+
process.stdout.write(`${VERSION2}
|
|
3025
3154
|
`);
|
|
3026
3155
|
return 0;
|
|
3027
3156
|
}
|
|
@@ -3050,6 +3179,9 @@ async function main(argv) {
|
|
|
3050
3179
|
}
|
|
3051
3180
|
return runAdmin(argv.slice(1));
|
|
3052
3181
|
}
|
|
3182
|
+
if (first === "mcp") {
|
|
3183
|
+
return runMcp(argv.slice(1));
|
|
3184
|
+
}
|
|
3053
3185
|
if (first === "import") {
|
|
3054
3186
|
return emit(runImport(argv.slice(1)));
|
|
3055
3187
|
}
|