@xopcai/xopc 0.0.8 → 0.0.11
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/extensions/telegram/src/plugin.js +1 -1
- package/dist/extensions/telegram/src/routing-integration.js +2 -2
- package/dist/extensions/weixin/src/plugin.js +1 -1
- package/dist/gateway/static/root/assets/{agents-BSNzJWbQ.js → agents-BdC4Y-HX.js} +2 -2
- package/dist/gateway/static/root/assets/agents-BdC4Y-HX.js.map +1 -0
- package/dist/gateway/static/root/assets/{apps-page-BKk9SB4D.js → apps-page-C-oaSHkm.js} +2 -2
- package/dist/gateway/static/root/assets/{apps-page-BKk9SB4D.js.map → apps-page-C-oaSHkm.js.map} +1 -1
- package/dist/gateway/static/root/assets/attachment-load-BDDlItdE.js +1 -0
- package/dist/gateway/static/root/assets/{channels-settings-_J6cQN6G.js → channels-settings-BqEUppPO.js} +2 -2
- package/dist/gateway/static/root/assets/{channels-settings-_J6cQN6G.js.map → channels-settings-BqEUppPO.js.map} +1 -1
- package/dist/gateway/static/root/assets/{chat-agents-api-DPb_0O8M.js → chat-agents-api-BhqjQ7iL.js} +2 -2
- package/dist/gateway/static/root/assets/{chat-agents-api-DPb_0O8M.js.map → chat-agents-api-BhqjQ7iL.js.map} +1 -1
- package/dist/gateway/static/root/assets/{cron-page-BUJOuuKX.js → cron-page-Cli49RKR.js} +2 -2
- package/dist/gateway/static/root/assets/{cron-page-BUJOuuKX.js.map → cron-page-Cli49RKR.js.map} +1 -1
- package/dist/gateway/static/root/assets/{cron-utils-Cn0YVg8x.js → cron-utils-Dkj-Ldpf.js} +2 -2
- package/dist/gateway/static/root/assets/{cron-utils-Cn0YVg8x.js.map → cron-utils-Dkj-Ldpf.js.map} +1 -1
- package/dist/gateway/static/root/assets/{electron-env-D9bm1FIu.js → electron-env-BDtJw9AY.js} +2 -2
- package/dist/gateway/static/root/assets/{electron-env-D9bm1FIu.js.map → electron-env-BDtJw9AY.js.map} +1 -1
- package/dist/gateway/static/root/assets/{extension-debug-page-DTz4O5Ua.js → extension-debug-page-BMcZlaxF.js} +2 -2
- package/dist/gateway/static/root/assets/{extension-debug-page-DTz4O5Ua.js.map → extension-debug-page-BMcZlaxF.js.map} +1 -1
- package/dist/gateway/static/root/assets/{extension-iframe-host-Cs1Kde9o.js → extension-iframe-host-D5HEF0KR.js} +2 -2
- package/dist/gateway/static/root/assets/{extension-iframe-host-Cs1Kde9o.js.map → extension-iframe-host-D5HEF0KR.js.map} +1 -1
- package/dist/gateway/static/root/assets/{extension-page-G52iX0Bo.js → extension-page-CXdCSSPl.js} +2 -2
- package/dist/gateway/static/root/assets/{extension-page-G52iX0Bo.js.map → extension-page-CXdCSSPl.js.map} +1 -1
- package/dist/gateway/static/root/assets/{extension-provider-CO2jxBA9.js → extension-provider-DZCZgQE2.js} +2 -2
- package/dist/gateway/static/root/assets/{extension-provider-CO2jxBA9.js.map → extension-provider-DZCZgQE2.js.map} +1 -1
- package/dist/gateway/static/root/assets/{extension-settings-page-D9Ul8uSt.js → extension-settings-page-CX6STpx3.js} +2 -2
- package/dist/gateway/static/root/assets/{extension-settings-page-D9Ul8uSt.js.map → extension-settings-page-CX6STpx3.js.map} +1 -1
- package/dist/gateway/static/root/assets/{gateway-config-swr-Bc8SVD15.js → gateway-config-swr-Cph02QZn.js} +2 -2
- package/dist/gateway/static/root/assets/{gateway-config-swr-Bc8SVD15.js.map → gateway-config-swr-Cph02QZn.js.map} +1 -1
- package/dist/gateway/static/root/assets/index-Bty3m0mS.css +2 -0
- package/dist/gateway/static/root/assets/{index-BXUJbteW.js → index-iTUyfzNr.js} +10 -10
- package/dist/gateway/static/root/assets/index-iTUyfzNr.js.map +1 -0
- package/dist/gateway/static/root/assets/{logs-page-5V25JkQY.js → logs-page-B9O5l3I8.js} +2 -2
- package/dist/gateway/static/root/assets/{logs-page-5V25JkQY.js.map → logs-page-B9O5l3I8.js.map} +1 -1
- package/dist/gateway/static/root/assets/{model-selector-he3aQfme.js → model-selector-BLiY_O25.js} +2 -2
- package/dist/gateway/static/root/assets/{model-selector-he3aQfme.js.map → model-selector-BLiY_O25.js.map} +1 -1
- package/dist/gateway/static/root/assets/page-header-store-BFpnFTed.js +2 -0
- package/dist/gateway/static/root/assets/{page-header-store-DJHD9Ean.js.map → page-header-store-BFpnFTed.js.map} +1 -1
- package/dist/gateway/static/root/assets/{session-api-n-4O5d9U.js → session-api-DEhQXWJg.js} +2 -2
- package/dist/gateway/static/root/assets/{session-api-n-4O5d9U.js.map → session-api-DEhQXWJg.js.map} +1 -1
- package/dist/gateway/static/root/assets/{session-working-directory-control-B6dHLvbr.js → session-working-directory-control-DKOtWs3-.js} +2 -2
- package/dist/gateway/static/root/assets/{session-working-directory-control-B6dHLvbr.js.map → session-working-directory-control-DKOtWs3-.js.map} +1 -1
- package/dist/gateway/static/root/assets/{sessions-page-rBUfTdm3.js → sessions-page-BYlWP1ep.js} +2 -2
- package/dist/gateway/static/root/assets/{sessions-page-rBUfTdm3.js.map → sessions-page-BYlWP1ep.js.map} +1 -1
- package/dist/gateway/static/root/assets/{settings-page-B3QrJm-E.js → settings-page-oCnIavdg.js} +2 -2
- package/dist/gateway/static/root/assets/settings-page-oCnIavdg.js.map +1 -0
- package/dist/gateway/static/root/assets/{skill-api-vxtE8kI6.js → skill-api-DWrn8Az0.js} +2 -2
- package/dist/gateway/static/root/assets/{skill-api-vxtE8kI6.js.map → skill-api-DWrn8Az0.js.map} +1 -1
- package/dist/gateway/static/root/assets/{skills-page-D36_O2Ub.js → skills-page-C59WQpM1.js} +2 -2
- package/dist/gateway/static/root/assets/{skills-page-D36_O2Ub.js.map → skills-page-C59WQpM1.js.map} +1 -1
- package/dist/gateway/static/root/assets/{theme-store-CmiSsYBd.js → theme-store-CywXkKml.js} +2 -2
- package/dist/gateway/static/root/assets/{theme-store-CmiSsYBd.js.map → theme-store-CywXkKml.js.map} +1 -1
- package/dist/gateway/static/root/assets/url-D7yWllI8.js +2 -0
- package/dist/gateway/static/root/assets/url-D7yWllI8.js.map +1 -0
- package/dist/gateway/static/root/assets/{useTranslation-DYORQ7x6.js → useTranslation-CACj0DBJ.js} +2 -2
- package/dist/gateway/static/root/assets/{useTranslation-DYORQ7x6.js.map → useTranslation-CACj0DBJ.js.map} +1 -1
- package/dist/gateway/static/root/index.html +15 -15
- package/dist/package.js +1 -1
- package/dist/src/agent/agent-manager.d.ts +1 -0
- package/dist/src/agent/agent-manager.js +20 -12
- package/dist/src/agent/agent-manager.js.map +1 -1
- package/dist/src/agent/background-review/run-background-review.js +2 -0
- package/dist/src/agent/background-review/run-background-review.js.map +1 -1
- package/dist/src/agent/child-agent-factory.js +2 -0
- package/dist/src/agent/child-agent-factory.js.map +1 -1
- package/dist/src/agent/context/expand-at-file-mentions.d.ts +4 -0
- package/dist/src/agent/context/expand-at-file-mentions.js +69 -0
- package/dist/src/agent/context/expand-at-file-mentions.js.map +1 -0
- package/dist/src/agent/context/workspace-seed.js +1 -1
- package/dist/src/agent/image/understanding/pi-ai-provider.js.map +1 -1
- package/dist/src/agent/ipc/bus.js +1 -1
- package/dist/src/agent/ipc/inbox.js +1 -1
- package/dist/src/agent/ipc/socket.js +1 -1
- package/dist/src/agent/memory/compaction.d.ts +1 -1
- package/dist/src/agent/memory/compaction.js +38 -11
- package/dist/src/agent/memory/compaction.js.map +1 -1
- package/dist/src/agent/messaging/command-handler.d.ts +13 -0
- package/dist/src/agent/messaging/command-handler.js +14 -2
- package/dist/src/agent/messaging/command-handler.js.map +1 -1
- package/dist/src/agent/models/manager.js +1 -1
- package/dist/src/agent/orchestration/agent-orchestrator.js +6 -1
- package/dist/src/agent/orchestration/agent-orchestrator.js.map +1 -1
- package/dist/src/agent/prompt/service-prompt-builder.js +1 -1
- package/dist/src/agent/service.d.ts +16 -1
- package/dist/src/agent/service.js +178 -20
- package/dist/src/agent/service.js.map +1 -1
- package/dist/src/agent/skills/format-skills-prompt.js.map +1 -1
- package/dist/src/agent/skills/index.js +1 -1
- package/dist/src/agent/skills/scanner.js +1 -1
- package/dist/src/agent/skills/skill-manage-ops.js +1 -1
- package/dist/src/agent/skills/skill-manage-ops.js.map +1 -1
- package/dist/src/agent/skills/skill-manager.js +1 -1
- package/dist/src/agent/tools/browser/tools.js.map +1 -1
- package/dist/src/agent/tools/factory.js +1 -1
- package/dist/src/agent/tools/image-tool.js.map +1 -1
- package/dist/src/agent/tools/send-media.js +1 -1
- package/dist/src/agent/tools/skill-manage-tool.js +1 -1
- package/dist/src/agent/tools/write.js +1 -1
- package/dist/src/auth/credentials.js +2 -2
- package/dist/src/auth/sync-provider-auth.js +1 -1
- package/dist/src/channels/attachments/inbound-persist.js +1 -1
- package/dist/src/channels/attachments/outbound-tts-persist.js +1 -1
- package/dist/src/channels/index.d.ts +1 -1
- package/dist/src/channels/index.js +2 -2
- package/dist/src/channels/pipeline.d.ts +8 -1
- package/dist/src/channels/pipeline.js +49 -4
- package/dist/src/channels/pipeline.js.map +1 -1
- package/dist/src/chat-commands/builtins/config.d.ts +4 -0
- package/dist/src/chat-commands/builtins/config.js +197 -0
- package/dist/src/chat-commands/builtins/config.js.map +1 -0
- package/dist/src/chat-commands/builtins/context.d.ts +4 -0
- package/dist/src/chat-commands/builtins/context.js +44 -0
- package/dist/src/chat-commands/builtins/context.js.map +1 -0
- package/dist/src/chat-commands/builtins/session.js +111 -0
- package/dist/src/chat-commands/builtins/session.js.map +1 -1
- package/dist/src/chat-commands/builtins/thinking.js +49 -21
- package/dist/src/chat-commands/builtins/thinking.js.map +1 -1
- package/dist/src/chat-commands/config-paths.d.ts +10 -0
- package/dist/src/chat-commands/config-paths.js +45 -0
- package/dist/src/chat-commands/config-paths.js.map +1 -0
- package/dist/src/chat-commands/config-value.d.ts +12 -0
- package/dist/src/chat-commands/config-value.js +53 -0
- package/dist/src/chat-commands/config-value.js.map +1 -0
- package/dist/src/chat-commands/context.d.ts +24 -1
- package/dist/src/chat-commands/context.js +41 -0
- package/dist/src/chat-commands/context.js.map +1 -1
- package/dist/src/chat-commands/index.d.ts +2 -0
- package/dist/src/chat-commands/index.js +5 -1
- package/dist/src/chat-commands/index.js.map +1 -1
- package/dist/src/chat-commands/types.d.ts +33 -1
- package/dist/src/cli/commands/agent/interactive.js +1 -1
- package/dist/src/cli/commands/agent/interactive.js.map +1 -1
- package/dist/src/cli/commands/agent.js +22 -10
- package/dist/src/cli/commands/agent.js.map +1 -1
- package/dist/src/cli/commands/auth.js.map +1 -1
- package/dist/src/cli/commands/doctor/checks/provider-auth.js +1 -1
- package/dist/src/cli/commands/extension.js +10 -0
- package/dist/src/cli/commands/extension.js.map +1 -1
- package/dist/src/cli/commands/init.js +3 -4
- package/dist/src/cli/commands/init.js.map +1 -1
- package/dist/src/cli/commands/session/utils.js.map +1 -1
- package/dist/src/cli/commands/update.d.ts +1 -0
- package/dist/src/cli/commands/update.js +171 -0
- package/dist/src/cli/commands/update.js.map +1 -0
- package/dist/src/cli/index.d.ts +1 -1
- package/dist/src/cli/index.js +3 -1
- package/dist/src/cli/index.js.map +1 -1
- package/dist/src/config/index.d.ts +1 -0
- package/dist/src/config/index.js +5 -4
- package/dist/src/config/index.js.map +1 -1
- package/dist/src/config/loader.js +1 -1
- package/dist/src/config/models-json.js +1 -1
- package/dist/src/config/paths.js.map +1 -1
- package/dist/src/config/profile.js +2 -2
- package/dist/src/config/runtime-overrides.d.ts +8 -0
- package/dist/src/config/runtime-overrides.js +40 -0
- package/dist/src/config/runtime-overrides.js.map +1 -0
- package/dist/src/config/schema.d.ts +35 -0
- package/dist/src/config/schema.js +19 -3
- package/dist/src/config/schema.js.map +1 -1
- package/dist/src/cron/executor.js +2 -2
- package/dist/src/cron/persistence.js +1 -1
- package/dist/src/cron/run-log-store.js +1 -1
- package/dist/src/extensions/health.js +1 -1
- package/dist/src/extensions/loader.d.ts +1 -1
- package/dist/src/extensions/loader.js +6 -9
- package/dist/src/extensions/loader.js.map +1 -1
- package/dist/src/extensions/lockfile.js +1 -1
- package/dist/src/extensions/sdk/index.js +6 -1
- package/dist/src/extensions/sdk/index.js.map +1 -0
- package/dist/src/gateway/agents-admin.js +2 -2
- package/dist/src/gateway/agents-admin.js.map +1 -1
- package/dist/src/gateway/hono/oauth.js +1 -1
- package/dist/src/gateway/hono/routes/config.js +1 -1
- package/dist/src/gateway/hono/routes/index.js +2 -0
- package/dist/src/gateway/hono/routes/index.js.map +1 -1
- package/dist/src/gateway/hono/routes/models.js +64 -11
- package/dist/src/gateway/hono/routes/models.js.map +1 -1
- package/dist/src/gateway/hono/routes/public-gateway.js +10 -0
- package/dist/src/gateway/hono/routes/public-gateway.js.map +1 -1
- package/dist/src/gateway/hono/routes/update.d.ts +3 -0
- package/dist/src/gateway/hono/routes/update.js +141 -0
- package/dist/src/gateway/hono/routes/update.js.map +1 -0
- package/dist/src/gateway/hono/routes/workspace.js +84 -4
- package/dist/src/gateway/hono/routes/workspace.js.map +1 -1
- package/dist/src/gateway/hono/sse.js +2 -2
- package/dist/src/gateway/service.d.ts +1 -0
- package/dist/src/gateway/service.js +16 -4
- package/dist/src/gateway/service.js.map +1 -1
- package/dist/src/gateway/workspace-fs-file-list.d.ts +5 -0
- package/dist/src/gateway/workspace-fs-file-list.js +56 -0
- package/dist/src/gateway/workspace-fs-file-list.js.map +1 -0
- package/dist/src/gateway/workspace-heartbeat-path.js +1 -1
- package/dist/src/gateway/workspace-ripgrep.d.ts +5 -0
- package/dist/src/gateway/workspace-ripgrep.js +88 -4
- package/dist/src/gateway/workspace-ripgrep.js.map +1 -1
- package/dist/src/infra/update-channels.d.ts +14 -0
- package/dist/src/infra/update-channels.js +30 -0
- package/dist/src/infra/update-channels.js.map +1 -0
- package/dist/src/infra/update-check.d.ts +53 -0
- package/dist/src/infra/update-check.js +155 -0
- package/dist/src/infra/update-check.js.map +1 -0
- package/dist/src/infra/update-runner.d.ts +18 -0
- package/dist/src/infra/update-runner.js +112 -0
- package/dist/src/infra/update-runner.js.map +1 -0
- package/dist/src/infra/update-startup.d.ts +20 -0
- package/dist/src/infra/update-startup.js +246 -0
- package/dist/src/infra/update-startup.js.map +1 -0
- package/dist/src/providers/extension-stream-bridge.d.ts +3 -0
- package/dist/src/providers/extension-stream-bridge.js +239 -0
- package/dist/src/providers/extension-stream-bridge.js.map +1 -0
- package/dist/src/providers/index.d.ts +7 -2
- package/dist/src/providers/index.js +77 -14
- package/dist/src/providers/index.js.map +1 -1
- package/dist/src/providers/model-registry.js +1 -1
- package/dist/src/providers/plugin-registry.js +92 -87
- package/dist/src/providers/plugin-registry.js.map +1 -1
- package/dist/src/session/chat-export.d.ts +5 -0
- package/dist/src/session/chat-export.js +35 -0
- package/dist/src/session/chat-export.js.map +1 -0
- package/dist/src/session/config-store.js +1 -1
- package/dist/src/session/manager.d.ts +1 -1
- package/dist/src/session/manager.js +2 -2
- package/dist/src/session/manager.js.map +1 -1
- package/dist/src/session/session-title.js +1 -1
- package/dist/src/session/store.d.ts +1 -1
- package/dist/src/session/store.js +5 -5
- package/dist/src/session/store.js.map +1 -1
- package/dist/src/utils/logger/audit.js +1 -1
- package/dist/src/utils/logger/log-store.js +1 -1
- package/dist/src/utils/logger/rotation.js +1 -1
- package/dist/src/voice/tts/audio.js +1 -1
- package/package.json +2 -1
- package/dist/gateway/static/root/assets/agents-BSNzJWbQ.js.map +0 -1
- package/dist/gateway/static/root/assets/attachment-load-DXcJLSWT.js +0 -1
- package/dist/gateway/static/root/assets/index-BXUJbteW.js.map +0 -1
- package/dist/gateway/static/root/assets/index-CQLMxWSA.css +0 -2
- package/dist/gateway/static/root/assets/page-header-store-DJHD9Ean.js +0 -2
- package/dist/gateway/static/root/assets/settings-page-B3QrJm-E.js.map +0 -1
- package/dist/gateway/static/root/assets/url-CtSqjF9J.js +0 -2
- package/dist/gateway/static/root/assets/url-CtSqjF9J.js.map +0 -1
|
@@ -0,0 +1,56 @@
|
|
|
1
|
+
import { createLogger } from "../utils/logger/index.js";
|
|
2
|
+
import { init_logger } from "../utils/logger.js";
|
|
3
|
+
import { join, relative } from "node:path";
|
|
4
|
+
import { readdir } from "node:fs/promises";
|
|
5
|
+
//#region src/gateway/workspace-fs-file-list.ts
|
|
6
|
+
init_logger();
|
|
7
|
+
const log = createLogger("WorkspaceFsFileList");
|
|
8
|
+
/** Match ripgrep `--glob` excludes in `runRipgrepListFiles` (keep list aligned where possible). */
|
|
9
|
+
const SKIP_DIR_NAMES = new Set(["node_modules", ".git"]);
|
|
10
|
+
/**
|
|
11
|
+
* Recursive workspace file list (relative POSIX paths). Used when ripgrep `--files` yields nothing
|
|
12
|
+
* (missing binary, spawn failure, or environment quirks) but the tree should still be searchable.
|
|
13
|
+
*/
|
|
14
|
+
async function listWorkspaceRelativeFilesFsFallback(workspaceRootAbs, maxFiles) {
|
|
15
|
+
const cap = Math.min(Math.max(maxFiles, 1), 2e5);
|
|
16
|
+
const out = [];
|
|
17
|
+
async function walk(dirAbs) {
|
|
18
|
+
if (out.length >= cap) return;
|
|
19
|
+
let entries;
|
|
20
|
+
try {
|
|
21
|
+
entries = await readdir(dirAbs, { withFileTypes: true });
|
|
22
|
+
} catch (err) {
|
|
23
|
+
log.debug({
|
|
24
|
+
err,
|
|
25
|
+
dirAbs
|
|
26
|
+
}, "fs list: readdir failed");
|
|
27
|
+
return;
|
|
28
|
+
}
|
|
29
|
+
for (const e of entries) {
|
|
30
|
+
if (out.length >= cap) return;
|
|
31
|
+
if (e.name.startsWith(".")) continue;
|
|
32
|
+
const full = join(dirAbs, e.name);
|
|
33
|
+
if (e.isDirectory()) {
|
|
34
|
+
if (SKIP_DIR_NAMES.has(e.name)) continue;
|
|
35
|
+
await walk(full);
|
|
36
|
+
} else if (e.isFile()) {
|
|
37
|
+
const rel = relative(workspaceRootAbs, full);
|
|
38
|
+
if (!rel || rel.startsWith("..")) continue;
|
|
39
|
+
out.push(rel.split("\\").join("/"));
|
|
40
|
+
}
|
|
41
|
+
}
|
|
42
|
+
}
|
|
43
|
+
try {
|
|
44
|
+
await walk(workspaceRootAbs);
|
|
45
|
+
} catch (err) {
|
|
46
|
+
log.warn({
|
|
47
|
+
err,
|
|
48
|
+
workspaceRootAbs
|
|
49
|
+
}, "fs list: walk failed");
|
|
50
|
+
}
|
|
51
|
+
return out;
|
|
52
|
+
}
|
|
53
|
+
//#endregion
|
|
54
|
+
export { listWorkspaceRelativeFilesFsFallback };
|
|
55
|
+
|
|
56
|
+
//# sourceMappingURL=workspace-fs-file-list.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"workspace-fs-file-list.js","names":[],"sources":["../../../src/gateway/workspace-fs-file-list.ts"],"sourcesContent":["import { readdir } from 'node:fs/promises';\nimport { join, relative } from 'node:path';\n\nimport { createLogger } from '../utils/logger.js';\n\nconst log = createLogger('WorkspaceFsFileList');\n\n/** Match ripgrep `--glob` excludes in `runRipgrepListFiles` (keep list aligned where possible). */\nconst SKIP_DIR_NAMES = new Set(['node_modules', '.git']);\n\n/**\n * Recursive workspace file list (relative POSIX paths). Used when ripgrep `--files` yields nothing\n * (missing binary, spawn failure, or environment quirks) but the tree should still be searchable.\n */\nexport async function listWorkspaceRelativeFilesFsFallback(\n workspaceRootAbs: string,\n maxFiles: number,\n): Promise<string[]> {\n const cap = Math.min(Math.max(maxFiles, 1), 200_000);\n const out: string[] = [];\n\n async function walk(dirAbs: string): Promise<void> {\n if (out.length >= cap) return;\n let entries;\n try {\n entries = await readdir(dirAbs, { withFileTypes: true });\n } catch (err) {\n log.debug({ err, dirAbs }, 'fs list: readdir failed');\n return;\n }\n for (const e of entries) {\n if (out.length >= cap) return;\n if (e.name.startsWith('.')) continue;\n const full = join(dirAbs, e.name);\n if (e.isDirectory()) {\n if (SKIP_DIR_NAMES.has(e.name)) continue;\n await walk(full);\n } else if (e.isFile()) {\n const rel = relative(workspaceRootAbs, full);\n if (!rel || rel.startsWith('..')) continue;\n out.push(rel.split('\\\\').join('/'));\n }\n }\n }\n\n try {\n await walk(workspaceRootAbs);\n } catch (err) {\n log.warn({ err, workspaceRootAbs }, 'fs list: walk failed');\n }\n return out;\n}\n"],"mappings":";;;;;aAGkD;AAElD,MAAM,MAAM,aAAa,sBAAsB;;AAG/C,MAAM,iBAAiB,IAAI,IAAI,CAAC,gBAAgB,OAAO,CAAC;;;;;AAMxD,eAAsB,qCACpB,kBACA,UACmB;CACnB,MAAM,MAAM,KAAK,IAAI,KAAK,IAAI,UAAU,EAAE,EAAE,IAAQ;CACpD,MAAM,MAAgB,EAAE;CAExB,eAAe,KAAK,QAA+B;AACjD,MAAI,IAAI,UAAU,IAAK;EACvB,IAAI;AACJ,MAAI;AACF,aAAU,MAAM,QAAQ,QAAQ,EAAE,eAAe,MAAM,CAAC;WACjD,KAAK;AACZ,OAAI,MAAM;IAAE;IAAK;IAAQ,EAAE,0BAA0B;AACrD;;AAEF,OAAK,MAAM,KAAK,SAAS;AACvB,OAAI,IAAI,UAAU,IAAK;AACvB,OAAI,EAAE,KAAK,WAAW,IAAI,CAAE;GAC5B,MAAM,OAAO,KAAK,QAAQ,EAAE,KAAK;AACjC,OAAI,EAAE,aAAa,EAAE;AACnB,QAAI,eAAe,IAAI,EAAE,KAAK,CAAE;AAChC,UAAM,KAAK,KAAK;cACP,EAAE,QAAQ,EAAE;IACrB,MAAM,MAAM,SAAS,kBAAkB,KAAK;AAC5C,QAAI,CAAC,OAAO,IAAI,WAAW,KAAK,CAAE;AAClC,QAAI,KAAK,IAAI,MAAM,KAAK,CAAC,KAAK,IAAI,CAAC;;;;AAKzC,KAAI;AACF,QAAM,KAAK,iBAAiB;UACrB,KAAK;AACZ,MAAI,KAAK;GAAE;GAAK;GAAkB,EAAE,uBAAuB;;AAE7D,QAAO"}
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import { init_agent_scope, resolveAgentBootstrapDir, resolveDefaultAgentId } from "../agent/agent-scope.js";
|
|
2
|
-
import { getWorkspacePath, init_schema } from "../config/schema.js";
|
|
3
2
|
import { WORKSPACE_FILES, init_paths } from "../config/paths.js";
|
|
3
|
+
import { getWorkspacePath, init_schema } from "../config/schema.js";
|
|
4
4
|
import { join } from "node:path";
|
|
5
5
|
//#region src/gateway/workspace-heartbeat-path.ts
|
|
6
6
|
init_schema();
|
|
@@ -7,3 +7,8 @@ export interface WorkspaceSearchHit {
|
|
|
7
7
|
}
|
|
8
8
|
/** Run ripgrep in a directory (absolute path). Returns empty array if rg fails to start. */
|
|
9
9
|
export declare function runRipgrepInDirectory(query: string, dirAbsPath: string): Promise<WorkspaceSearchHit[]>;
|
|
10
|
+
/**
|
|
11
|
+
* List workspace-relative file paths via ripgrep `--files` (respects .gitignore; fast on large trees).
|
|
12
|
+
* Returns POSIX paths relative to `dirAbsPath`.
|
|
13
|
+
*/
|
|
14
|
+
export declare function runRipgrepListFiles(dirAbsPath: string): Promise<string[]>;
|
|
@@ -1,11 +1,28 @@
|
|
|
1
|
+
import { createLogger } from "../utils/logger/index.js";
|
|
2
|
+
import { init_logger } from "../utils/logger.js";
|
|
3
|
+
import { existsSync } from "node:fs";
|
|
1
4
|
import { spawn } from "node:child_process";
|
|
2
5
|
//#region src/gateway/workspace-ripgrep.ts
|
|
6
|
+
init_logger();
|
|
7
|
+
const log = createLogger("WorkspaceRipgrep");
|
|
8
|
+
function isEnoent(err) {
|
|
9
|
+
return err !== null && typeof err === "object" && err.code === "ENOENT";
|
|
10
|
+
}
|
|
11
|
+
let cachedRipgrepBin;
|
|
12
|
+
/**
|
|
13
|
+
* Prefer `@vscode/ripgrep` when its postinstall placed `bin/rg`; otherwise use `rg` on PATH.
|
|
14
|
+
* (Bundled path can be ENOENT if postinstall was skipped or the binary was never downloaded.)
|
|
15
|
+
*/
|
|
3
16
|
async function resolveRipgrepBinary() {
|
|
17
|
+
if (cachedRipgrepBin) return cachedRipgrepBin;
|
|
18
|
+
let bin = "rg";
|
|
4
19
|
try {
|
|
5
20
|
const { rgPath } = await import("@vscode/ripgrep");
|
|
6
|
-
if (typeof rgPath === "string" && rgPath.length > 0)
|
|
21
|
+
if (typeof rgPath === "string" && rgPath.length > 0 && existsSync(rgPath)) bin = rgPath;
|
|
22
|
+
else if (typeof rgPath === "string" && rgPath.length > 0) log.debug({ rgPath }, "@vscode/ripgrep binary not on disk; will try rg on PATH");
|
|
7
23
|
} catch {}
|
|
8
|
-
|
|
24
|
+
cachedRipgrepBin = bin;
|
|
25
|
+
return bin;
|
|
9
26
|
}
|
|
10
27
|
/** Run ripgrep in a directory (absolute path). Returns empty array if rg fails to start. */
|
|
11
28
|
function runRipgrepInDirectory(query, dirAbsPath) {
|
|
@@ -52,11 +69,78 @@ function runRipgrepInDirectory(query, dirAbsPath) {
|
|
|
52
69
|
}
|
|
53
70
|
});
|
|
54
71
|
rg.on("close", () => resolve(results));
|
|
55
|
-
rg.on("error", () =>
|
|
72
|
+
rg.on("error", (err) => {
|
|
73
|
+
if (!isEnoent(err)) log.warn({
|
|
74
|
+
err,
|
|
75
|
+
query,
|
|
76
|
+
dir: dirAbsPath,
|
|
77
|
+
rg: rgExecutable
|
|
78
|
+
}, "ripgrep in-directory: spawn failed");
|
|
79
|
+
else log.debug({
|
|
80
|
+
dir: dirAbsPath,
|
|
81
|
+
rg: rgExecutable
|
|
82
|
+
}, "ripgrep not on PATH; skipping in-directory search");
|
|
83
|
+
resolve([]);
|
|
84
|
+
});
|
|
85
|
+
});
|
|
86
|
+
})();
|
|
87
|
+
}
|
|
88
|
+
/**
|
|
89
|
+
* List workspace-relative file paths via ripgrep `--files` (respects .gitignore; fast on large trees).
|
|
90
|
+
* Returns POSIX paths relative to `dirAbsPath`.
|
|
91
|
+
*/
|
|
92
|
+
function runRipgrepListFiles(dirAbsPath) {
|
|
93
|
+
return (async () => {
|
|
94
|
+
const rgExecutable = await resolveRipgrepBinary();
|
|
95
|
+
return await new Promise((resolve) => {
|
|
96
|
+
const rg = spawn(rgExecutable, [
|
|
97
|
+
"--files",
|
|
98
|
+
"--glob",
|
|
99
|
+
"!**/node_modules/**",
|
|
100
|
+
"--glob",
|
|
101
|
+
"!.git/**",
|
|
102
|
+
"."
|
|
103
|
+
], {
|
|
104
|
+
shell: false,
|
|
105
|
+
cwd: dirAbsPath
|
|
106
|
+
});
|
|
107
|
+
const lines = [];
|
|
108
|
+
let buffer = "";
|
|
109
|
+
rg.stdout.on("data", (data) => {
|
|
110
|
+
buffer += data.toString();
|
|
111
|
+
const parts = buffer.split(/\r?\n/);
|
|
112
|
+
buffer = parts.pop() ?? "";
|
|
113
|
+
for (const line of parts) {
|
|
114
|
+
const t = line.trim();
|
|
115
|
+
if (t) lines.push(t.replace(/\\/g, "/"));
|
|
116
|
+
}
|
|
117
|
+
});
|
|
118
|
+
rg.on("close", (code) => {
|
|
119
|
+
const tail = buffer.trim();
|
|
120
|
+
if (tail) lines.push(tail.replace(/\\/g, "/"));
|
|
121
|
+
if (code !== 0 && lines.length === 0) log.debug({
|
|
122
|
+
code,
|
|
123
|
+
cwd: dirAbsPath,
|
|
124
|
+
rg: rgExecutable
|
|
125
|
+
}, "ripgrep --files: non-zero exit, no output (using fs fallback if any)");
|
|
126
|
+
resolve(lines);
|
|
127
|
+
});
|
|
128
|
+
rg.on("error", (err) => {
|
|
129
|
+
if (isEnoent(err)) log.debug({
|
|
130
|
+
cwd: dirAbsPath,
|
|
131
|
+
rg: rgExecutable
|
|
132
|
+
}, "ripgrep binary not found; workspace file search will use fs fallback when needed");
|
|
133
|
+
else log.warn({
|
|
134
|
+
err,
|
|
135
|
+
cwd: dirAbsPath,
|
|
136
|
+
rg: rgExecutable
|
|
137
|
+
}, "ripgrep --files failed to start");
|
|
138
|
+
resolve([]);
|
|
139
|
+
});
|
|
56
140
|
});
|
|
57
141
|
})();
|
|
58
142
|
}
|
|
59
143
|
//#endregion
|
|
60
|
-
export { runRipgrepInDirectory };
|
|
144
|
+
export { runRipgrepInDirectory, runRipgrepListFiles };
|
|
61
145
|
|
|
62
146
|
//# sourceMappingURL=workspace-ripgrep.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"workspace-ripgrep.js","names":[],"sources":["../../../src/gateway/workspace-ripgrep.ts"],"sourcesContent":["import { spawn } from 'node:child_process';\n\
|
|
1
|
+
{"version":3,"file":"workspace-ripgrep.js","names":[],"sources":["../../../src/gateway/workspace-ripgrep.ts"],"sourcesContent":["import { spawn } from 'node:child_process';\nimport { existsSync } from 'node:fs';\n\nimport { createLogger } from '../utils/logger.js';\n\nconst log = createLogger('WorkspaceRipgrep');\n\nfunction isEnoent(err: unknown): boolean {\n return err !== null && typeof err === 'object' && (err as NodeJS.ErrnoException).code === 'ENOENT';\n}\n\nlet cachedRipgrepBin: string | undefined;\n\n/**\n * Prefer `@vscode/ripgrep` when its postinstall placed `bin/rg`; otherwise use `rg` on PATH.\n * (Bundled path can be ENOENT if postinstall was skipped or the binary was never downloaded.)\n */\nasync function resolveRipgrepBinary(): Promise<string> {\n if (cachedRipgrepBin) return cachedRipgrepBin;\n let bin = 'rg';\n try {\n const { rgPath } = await import('@vscode/ripgrep');\n if (typeof rgPath === 'string' && rgPath.length > 0 && existsSync(rgPath)) {\n bin = rgPath;\n } else if (typeof rgPath === 'string' && rgPath.length > 0) {\n log.debug({ rgPath }, '@vscode/ripgrep binary not on disk; will try rg on PATH');\n }\n } catch {\n // pnpm may skip @vscode/ripgrep postinstall; package dir can be missing.\n }\n cachedRipgrepBin = bin;\n return bin;\n}\n\nexport interface WorkspaceSearchHit {\n filePath: string;\n lineNumber: number;\n lineContent: string;\n matchStart: number;\n matchEnd: number;\n}\n\n/** Run ripgrep in a directory (absolute path). Returns empty array if rg fails to start. */\nexport function runRipgrepInDirectory(query: string, dirAbsPath: string): Promise<WorkspaceSearchHit[]> {\n return (async () => {\n const rgExecutable = await resolveRipgrepBinary();\n\n return await new Promise<WorkspaceSearchHit[]>((resolve) => {\n const args = [\n '--json',\n '--smart-case',\n '--max-count',\n '50',\n '--glob',\n '*.md',\n '--glob',\n '*.txt',\n query,\n dirAbsPath,\n ];\n\n const rg = spawn(rgExecutable, args, { shell: false });\n const results: WorkspaceSearchHit[] = [];\n let buffer = '';\n\n rg.stdout.on('data', (data: Buffer) => {\n buffer += data.toString();\n const lines = buffer.split('\\n');\n buffer = lines.pop() ?? '';\n\n for (const line of lines) {\n if (!line.trim()) continue;\n try {\n const parsed = JSON.parse(line) as {\n type?: string;\n data?: {\n path?: { text?: string };\n lines?: { text?: string };\n line_number?: number;\n submatches?: Array<{ start?: number; end?: number }>;\n };\n };\n if (parsed.type === 'match' && parsed.data) {\n const d = parsed.data;\n const pathText = d.path?.text ?? '';\n const lineContent = d.lines?.text ?? '';\n const lineNumber = d.line_number ?? 0;\n const sm = d.submatches?.[0];\n results.push({\n filePath: pathText,\n lineNumber,\n lineContent: lineContent.trimEnd(),\n matchStart: sm?.start ?? 0,\n matchEnd: sm?.end ?? 0,\n });\n }\n } catch {\n /* skip */\n }\n }\n });\n\n rg.on('close', () => resolve(results));\n rg.on('error', (err) => {\n if (!isEnoent(err)) {\n log.warn({ err, query, dir: dirAbsPath, rg: rgExecutable }, 'ripgrep in-directory: spawn failed');\n } else {\n log.debug({ dir: dirAbsPath, rg: rgExecutable }, 'ripgrep not on PATH; skipping in-directory search');\n }\n resolve([]);\n });\n });\n })();\n}\n\n/**\n * List workspace-relative file paths via ripgrep `--files` (respects .gitignore; fast on large trees).\n * Returns POSIX paths relative to `dirAbsPath`.\n */\nexport function runRipgrepListFiles(dirAbsPath: string): Promise<string[]> {\n return (async () => {\n const rgExecutable = await resolveRipgrepBinary();\n\n return await new Promise<string[]>((resolve) => {\n const args = ['--files', '--glob', '!**/node_modules/**', '--glob', '!.git/**', '.'];\n const rg = spawn(rgExecutable, args, { shell: false, cwd: dirAbsPath });\n const lines: string[] = [];\n let buffer = '';\n\n rg.stdout.on('data', (data: Buffer) => {\n buffer += data.toString();\n const parts = buffer.split(/\\r?\\n/);\n buffer = parts.pop() ?? '';\n for (const line of parts) {\n const t = line.trim();\n if (t) lines.push(t.replace(/\\\\/g, '/'));\n }\n });\n\n rg.on('close', (code) => {\n const tail = buffer.trim();\n if (tail) lines.push(tail.replace(/\\\\/g, '/'));\n if (code !== 0 && lines.length === 0) {\n log.debug({ code, cwd: dirAbsPath, rg: rgExecutable }, 'ripgrep --files: non-zero exit, no output (using fs fallback if any)');\n }\n resolve(lines);\n });\n rg.on('error', (err) => {\n if (isEnoent(err)) {\n log.debug(\n { cwd: dirAbsPath, rg: rgExecutable },\n 'ripgrep binary not found; workspace file search will use fs fallback when needed',\n );\n } else {\n log.warn({ err, cwd: dirAbsPath, rg: rgExecutable }, 'ripgrep --files failed to start');\n }\n resolve([]);\n });\n });\n })();\n}\n"],"mappings":"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"}
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
export type UpdateChannel = 'stable' | 'beta' | 'dev';
|
|
2
|
+
export declare const DEFAULT_PACKAGE_CHANNEL: UpdateChannel;
|
|
3
|
+
export declare const DEFAULT_GIT_CHANNEL: UpdateChannel;
|
|
4
|
+
/**
|
|
5
|
+
* Map update channel to the npm dist-tag used for querying registry.
|
|
6
|
+
* stable → latest, beta → beta, dev → dev
|
|
7
|
+
*/
|
|
8
|
+
export declare function channelToNpmTag(channel: UpdateChannel): string;
|
|
9
|
+
/** Normalize a user-provided string to a valid UpdateChannel, or null. */
|
|
10
|
+
export declare function normalizeUpdateChannel(value?: string | null): UpdateChannel | null;
|
|
11
|
+
/** Return true if a version/tag string contains a beta prerelease identifier. */
|
|
12
|
+
export declare function isBetaVersion(version: string): boolean;
|
|
13
|
+
/** Return true if a version string is a stable release (no beta marker in this heuristic). */
|
|
14
|
+
export declare function isStableVersion(version: string): boolean;
|
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
//#region src/infra/update-channels.ts
|
|
2
|
+
const DEFAULT_PACKAGE_CHANNEL = "stable";
|
|
3
|
+
const DEFAULT_GIT_CHANNEL = "dev";
|
|
4
|
+
/**
|
|
5
|
+
* Map update channel to the npm dist-tag used for querying registry.
|
|
6
|
+
* stable → latest, beta → beta, dev → dev
|
|
7
|
+
*/
|
|
8
|
+
function channelToNpmTag(channel) {
|
|
9
|
+
if (channel === "beta") return "beta";
|
|
10
|
+
if (channel === "dev") return "dev";
|
|
11
|
+
return "latest";
|
|
12
|
+
}
|
|
13
|
+
/** Normalize a user-provided string to a valid UpdateChannel, or null. */
|
|
14
|
+
function normalizeUpdateChannel(value) {
|
|
15
|
+
const normalized = value?.trim().toLowerCase();
|
|
16
|
+
if (normalized === "stable" || normalized === "beta" || normalized === "dev") return normalized;
|
|
17
|
+
return null;
|
|
18
|
+
}
|
|
19
|
+
/** Return true if a version/tag string contains a beta prerelease identifier. */
|
|
20
|
+
function isBetaVersion(version) {
|
|
21
|
+
return /(?:^|[.-])beta(?:[.-]|$)/i.test(version);
|
|
22
|
+
}
|
|
23
|
+
/** Return true if a version string is a stable release (no beta marker in this heuristic). */
|
|
24
|
+
function isStableVersion(version) {
|
|
25
|
+
return !isBetaVersion(version);
|
|
26
|
+
}
|
|
27
|
+
//#endregion
|
|
28
|
+
export { DEFAULT_GIT_CHANNEL, DEFAULT_PACKAGE_CHANNEL, channelToNpmTag, isBetaVersion, isStableVersion, normalizeUpdateChannel };
|
|
29
|
+
|
|
30
|
+
//# sourceMappingURL=update-channels.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"update-channels.js","names":[],"sources":["../../../src/infra/update-channels.ts"],"sourcesContent":["// src/infra/update-channels.ts\n\nexport type UpdateChannel = 'stable' | 'beta' | 'dev';\n\nexport const DEFAULT_PACKAGE_CHANNEL: UpdateChannel = 'stable';\nexport const DEFAULT_GIT_CHANNEL: UpdateChannel = 'dev';\n\n/**\n * Map update channel to the npm dist-tag used for querying registry.\n * stable → latest, beta → beta, dev → dev\n */\nexport function channelToNpmTag(channel: UpdateChannel): string {\n if (channel === 'beta') return 'beta';\n if (channel === 'dev') return 'dev';\n return 'latest';\n}\n\n/** Normalize a user-provided string to a valid UpdateChannel, or null. */\nexport function normalizeUpdateChannel(value?: string | null): UpdateChannel | null {\n const normalized = value?.trim().toLowerCase();\n if (normalized === 'stable' || normalized === 'beta' || normalized === 'dev') {\n return normalized;\n }\n return null;\n}\n\n/** Return true if a version/tag string contains a beta prerelease identifier. */\nexport function isBetaVersion(version: string): boolean {\n return /(?:^|[.-])beta(?:[.-]|$)/i.test(version);\n}\n\n/** Return true if a version string is a stable release (no beta marker in this heuristic). */\nexport function isStableVersion(version: string): boolean {\n return !isBetaVersion(version);\n}\n"],"mappings":";AAIA,MAAa,0BAAyC;AACtD,MAAa,sBAAqC;;;;;AAMlD,SAAgB,gBAAgB,SAAgC;AAC9D,KAAI,YAAY,OAAQ,QAAO;AAC/B,KAAI,YAAY,MAAO,QAAO;AAC9B,QAAO;;;AAIT,SAAgB,uBAAuB,OAA6C;CAClF,MAAM,aAAa,OAAO,MAAM,CAAC,aAAa;AAC9C,KAAI,eAAe,YAAY,eAAe,UAAU,eAAe,MACrE,QAAO;AAET,QAAO;;;AAIT,SAAgB,cAAc,SAA0B;AACtD,QAAO,4BAA4B,KAAK,QAAQ;;;AAIlD,SAAgB,gBAAgB,SAA0B;AACxD,QAAO,CAAC,cAAc,QAAQ"}
|
|
@@ -0,0 +1,53 @@
|
|
|
1
|
+
import { type UpdateChannel } from './update-channels.js';
|
|
2
|
+
export type InstallKind = 'git' | 'package' | 'unknown';
|
|
3
|
+
export type NpmTagResult = {
|
|
4
|
+
tag: string;
|
|
5
|
+
version: string | null;
|
|
6
|
+
error?: string;
|
|
7
|
+
};
|
|
8
|
+
export type UpdateCheckResult = {
|
|
9
|
+
installKind: InstallKind;
|
|
10
|
+
root: string | null;
|
|
11
|
+
};
|
|
12
|
+
export type UpdateAvailable = {
|
|
13
|
+
currentVersion: string;
|
|
14
|
+
latestVersion: string;
|
|
15
|
+
channel: string;
|
|
16
|
+
};
|
|
17
|
+
/**
|
|
18
|
+
* Fetch the version published under a specific npm dist-tag.
|
|
19
|
+
* Uses the abbreviated packument endpoint: `GET /<pkg>/<tag>`.
|
|
20
|
+
*/
|
|
21
|
+
export declare function fetchNpmTagVersion(params: {
|
|
22
|
+
tag: string;
|
|
23
|
+
timeoutMs?: number;
|
|
24
|
+
}): Promise<NpmTagResult>;
|
|
25
|
+
/**
|
|
26
|
+
* Resolve the best version for the given update channel.
|
|
27
|
+
* For beta channel: if the beta tag version is older than latest, return latest instead.
|
|
28
|
+
*/
|
|
29
|
+
export declare function resolveNpmChannelTag(params: {
|
|
30
|
+
channel: UpdateChannel;
|
|
31
|
+
timeoutMs?: number;
|
|
32
|
+
}): Promise<{
|
|
33
|
+
tag: string;
|
|
34
|
+
version: string | null;
|
|
35
|
+
}>;
|
|
36
|
+
/**
|
|
37
|
+
* Compare two semver strings. Returns:
|
|
38
|
+
* -1 if a < b, 0 if equal, 1 if a > b, null if either is unparseable.
|
|
39
|
+
* Prerelease versions are considered older than the same version without prerelease.
|
|
40
|
+
*/
|
|
41
|
+
export declare function compareSemver(a: string | null, b: string | null): number | null;
|
|
42
|
+
/**
|
|
43
|
+
* Detect whether the current process is running from a git checkout or npm-installed package.
|
|
44
|
+
* Checks for `.git` directory in the package root.
|
|
45
|
+
*/
|
|
46
|
+
export declare function detectInstallKind(packageRoot: string): Promise<InstallKind>;
|
|
47
|
+
/**
|
|
48
|
+
* Resolve the xopc package root directory.
|
|
49
|
+
* Walks up from this file to find package.json with name @xopcai/xopc.
|
|
50
|
+
*/
|
|
51
|
+
export declare function resolvePackageRoot(): Promise<string | null>;
|
|
52
|
+
/** Get the current running version from package.json. */
|
|
53
|
+
export declare function getCurrentVersion(): string;
|
|
@@ -0,0 +1,155 @@
|
|
|
1
|
+
import { PACKAGE_VERSION, init_package_version } from "../package-version.js";
|
|
2
|
+
import { channelToNpmTag } from "./update-channels.js";
|
|
3
|
+
import { dirname, join } from "node:path";
|
|
4
|
+
import { access, readFile } from "node:fs/promises";
|
|
5
|
+
import { fileURLToPath } from "node:url";
|
|
6
|
+
//#region src/infra/update-check.ts
|
|
7
|
+
init_package_version();
|
|
8
|
+
const REGISTRY_BASE = "https://registry.npmjs.org";
|
|
9
|
+
const PACKAGE_NAME = "@xopcai/xopc";
|
|
10
|
+
const REGISTRY_TIMEOUT_MS = 3500;
|
|
11
|
+
/**
|
|
12
|
+
* Fetch the version published under a specific npm dist-tag.
|
|
13
|
+
* Uses the abbreviated packument endpoint: `GET /<pkg>/<tag>`.
|
|
14
|
+
*/
|
|
15
|
+
async function fetchNpmTagVersion(params) {
|
|
16
|
+
const timeoutMs = params.timeoutMs ?? REGISTRY_TIMEOUT_MS;
|
|
17
|
+
const url = `${REGISTRY_BASE}/${encodeURIComponent(PACKAGE_NAME).replace("%40", "@")}/${encodeURIComponent(params.tag)}`;
|
|
18
|
+
try {
|
|
19
|
+
const response = await fetch(url, { signal: AbortSignal.timeout(timeoutMs) });
|
|
20
|
+
if (!response.ok) return {
|
|
21
|
+
tag: params.tag,
|
|
22
|
+
version: null,
|
|
23
|
+
error: `HTTP ${response.status}`
|
|
24
|
+
};
|
|
25
|
+
const json = await response.json();
|
|
26
|
+
const version = typeof json?.version === "string" ? json.version : null;
|
|
27
|
+
return {
|
|
28
|
+
tag: params.tag,
|
|
29
|
+
version
|
|
30
|
+
};
|
|
31
|
+
} catch (err) {
|
|
32
|
+
return {
|
|
33
|
+
tag: params.tag,
|
|
34
|
+
version: null,
|
|
35
|
+
error: String(err)
|
|
36
|
+
};
|
|
37
|
+
}
|
|
38
|
+
}
|
|
39
|
+
/**
|
|
40
|
+
* Resolve the best version for the given update channel.
|
|
41
|
+
* For beta channel: if the beta tag version is older than latest, return latest instead.
|
|
42
|
+
*/
|
|
43
|
+
async function resolveNpmChannelTag(params) {
|
|
44
|
+
const channelTag = channelToNpmTag(params.channel);
|
|
45
|
+
const channelResult = await fetchNpmTagVersion({
|
|
46
|
+
tag: channelTag,
|
|
47
|
+
timeoutMs: params.timeoutMs
|
|
48
|
+
});
|
|
49
|
+
if (params.channel !== "beta") return {
|
|
50
|
+
tag: channelTag,
|
|
51
|
+
version: channelResult.version
|
|
52
|
+
};
|
|
53
|
+
const latestResult = await fetchNpmTagVersion({
|
|
54
|
+
tag: "latest",
|
|
55
|
+
timeoutMs: params.timeoutMs
|
|
56
|
+
});
|
|
57
|
+
if (!latestResult.version) return {
|
|
58
|
+
tag: channelTag,
|
|
59
|
+
version: channelResult.version
|
|
60
|
+
};
|
|
61
|
+
if (!channelResult.version) return {
|
|
62
|
+
tag: "latest",
|
|
63
|
+
version: latestResult.version
|
|
64
|
+
};
|
|
65
|
+
const comparison = compareSemver(channelResult.version, latestResult.version);
|
|
66
|
+
if (comparison !== null && comparison < 0) return {
|
|
67
|
+
tag: "latest",
|
|
68
|
+
version: latestResult.version
|
|
69
|
+
};
|
|
70
|
+
return {
|
|
71
|
+
tag: channelTag,
|
|
72
|
+
version: channelResult.version
|
|
73
|
+
};
|
|
74
|
+
}
|
|
75
|
+
/**
|
|
76
|
+
* Parse a version string into comparable numeric parts.
|
|
77
|
+
* Handles formats like "1.2.3", "1.2.3-beta.1".
|
|
78
|
+
* Returns null for unparseable strings.
|
|
79
|
+
*/
|
|
80
|
+
function parseSemverParts(version) {
|
|
81
|
+
const match = /^v?(\d+)\.(\d+)\.(\d+)(?:-(.+))?$/.exec(version.trim());
|
|
82
|
+
if (!match) return null;
|
|
83
|
+
return {
|
|
84
|
+
major: parseInt(match[1], 10),
|
|
85
|
+
minor: parseInt(match[2], 10),
|
|
86
|
+
patch: parseInt(match[3], 10),
|
|
87
|
+
prerelease: match[4] ?? null
|
|
88
|
+
};
|
|
89
|
+
}
|
|
90
|
+
/**
|
|
91
|
+
* Compare two semver strings. Returns:
|
|
92
|
+
* -1 if a < b, 0 if equal, 1 if a > b, null if either is unparseable.
|
|
93
|
+
* Prerelease versions are considered older than the same version without prerelease.
|
|
94
|
+
*/
|
|
95
|
+
function compareSemver(a, b) {
|
|
96
|
+
if (!a || !b) return null;
|
|
97
|
+
const parsedA = parseSemverParts(a);
|
|
98
|
+
const parsedB = parseSemverParts(b);
|
|
99
|
+
if (!parsedA || !parsedB) return null;
|
|
100
|
+
for (const field of [
|
|
101
|
+
"major",
|
|
102
|
+
"minor",
|
|
103
|
+
"patch"
|
|
104
|
+
]) if (parsedA[field] !== parsedB[field]) return parsedA[field] < parsedB[field] ? -1 : 1;
|
|
105
|
+
if (parsedA.prerelease === null && parsedB.prerelease === null) return 0;
|
|
106
|
+
if (parsedA.prerelease !== null && parsedB.prerelease === null) return -1;
|
|
107
|
+
if (parsedA.prerelease === null && parsedB.prerelease !== null) return 1;
|
|
108
|
+
if (parsedA.prerelease < parsedB.prerelease) return -1;
|
|
109
|
+
if (parsedA.prerelease > parsedB.prerelease) return 1;
|
|
110
|
+
return 0;
|
|
111
|
+
}
|
|
112
|
+
/**
|
|
113
|
+
* Detect whether the current process is running from a git checkout or npm-installed package.
|
|
114
|
+
* Checks for `.git` directory in the package root.
|
|
115
|
+
*/
|
|
116
|
+
async function detectInstallKind(packageRoot) {
|
|
117
|
+
try {
|
|
118
|
+
await access(join(packageRoot, ".git"));
|
|
119
|
+
return "git";
|
|
120
|
+
} catch {
|
|
121
|
+
try {
|
|
122
|
+
await access(join(packageRoot, "package.json"));
|
|
123
|
+
return "package";
|
|
124
|
+
} catch {
|
|
125
|
+
return "unknown";
|
|
126
|
+
}
|
|
127
|
+
}
|
|
128
|
+
}
|
|
129
|
+
/**
|
|
130
|
+
* Resolve the xopc package root directory.
|
|
131
|
+
* Walks up from this file to find package.json with name @xopcai/xopc.
|
|
132
|
+
*/
|
|
133
|
+
async function resolvePackageRoot() {
|
|
134
|
+
let current = dirname(fileURLToPath(import.meta.url));
|
|
135
|
+
for (let depth = 0; depth < 20; depth++) {
|
|
136
|
+
const pkgPath = join(current, "package.json");
|
|
137
|
+
try {
|
|
138
|
+
const raw = await readFile(pkgPath, "utf-8");
|
|
139
|
+
const parsed = JSON.parse(raw);
|
|
140
|
+
if (typeof parsed.name === "string" && parsed.name === PACKAGE_NAME) return current;
|
|
141
|
+
} catch {}
|
|
142
|
+
const parent = dirname(current);
|
|
143
|
+
if (parent === current) return null;
|
|
144
|
+
current = parent;
|
|
145
|
+
}
|
|
146
|
+
return null;
|
|
147
|
+
}
|
|
148
|
+
/** Get the current running version from package.json. */
|
|
149
|
+
function getCurrentVersion() {
|
|
150
|
+
return PACKAGE_VERSION;
|
|
151
|
+
}
|
|
152
|
+
//#endregion
|
|
153
|
+
export { compareSemver, detectInstallKind, fetchNpmTagVersion, getCurrentVersion, resolveNpmChannelTag, resolvePackageRoot };
|
|
154
|
+
|
|
155
|
+
//# sourceMappingURL=update-check.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"update-check.js","names":[],"sources":["../../../src/infra/update-check.ts"],"sourcesContent":["// src/infra/update-check.ts\n\nimport { access, readFile } from 'node:fs/promises';\nimport { dirname, join } from 'node:path';\nimport { fileURLToPath } from 'node:url';\n\nimport { PACKAGE_VERSION } from '../package-version.js';\n\nimport { channelToNpmTag, type UpdateChannel } from './update-channels.js';\n\n// --- Types ---\n\nexport type InstallKind = 'git' | 'package' | 'unknown';\n\nexport type NpmTagResult = {\n tag: string;\n version: string | null;\n error?: string;\n};\n\nexport type UpdateCheckResult = {\n installKind: InstallKind;\n root: string | null;\n};\n\nexport type UpdateAvailable = {\n currentVersion: string;\n latestVersion: string;\n channel: string;\n};\n\n// --- npm Registry ---\n\nconst REGISTRY_BASE = 'https://registry.npmjs.org';\nconst PACKAGE_NAME = '@xopcai/xopc';\nconst REGISTRY_TIMEOUT_MS = 3500;\n\n/**\n * Fetch the version published under a specific npm dist-tag.\n * Uses the abbreviated packument endpoint: `GET /<pkg>/<tag>`.\n */\nexport async function fetchNpmTagVersion(params: {\n tag: string;\n timeoutMs?: number;\n}): Promise<NpmTagResult> {\n const timeoutMs = params.timeoutMs ?? REGISTRY_TIMEOUT_MS;\n const encodedName = encodeURIComponent(PACKAGE_NAME).replace('%40', '@');\n const url = `${REGISTRY_BASE}/${encodedName}/${encodeURIComponent(params.tag)}`;\n try {\n const response = await fetch(url, {\n signal: AbortSignal.timeout(timeoutMs),\n });\n if (!response.ok) {\n return { tag: params.tag, version: null, error: `HTTP ${response.status}` };\n }\n const json = (await response.json()) as { version?: unknown };\n const version = typeof json?.version === 'string' ? json.version : null;\n return { tag: params.tag, version };\n } catch (err) {\n return { tag: params.tag, version: null, error: String(err) };\n }\n}\n\n/**\n * Resolve the best version for the given update channel.\n * For beta channel: if the beta tag version is older than latest, return latest instead.\n */\nexport async function resolveNpmChannelTag(params: {\n channel: UpdateChannel;\n timeoutMs?: number;\n}): Promise<{ tag: string; version: string | null }> {\n const channelTag = channelToNpmTag(params.channel);\n const channelResult = await fetchNpmTagVersion({ tag: channelTag, timeoutMs: params.timeoutMs });\n\n if (params.channel !== 'beta') {\n return { tag: channelTag, version: channelResult.version };\n }\n\n // For beta: also check latest, return whichever is newer\n const latestResult = await fetchNpmTagVersion({ tag: 'latest', timeoutMs: params.timeoutMs });\n if (!latestResult.version) {\n return { tag: channelTag, version: channelResult.version };\n }\n if (!channelResult.version) {\n return { tag: 'latest', version: latestResult.version };\n }\n const comparison = compareSemver(channelResult.version, latestResult.version);\n if (comparison !== null && comparison < 0) {\n return { tag: 'latest', version: latestResult.version };\n }\n return { tag: channelTag, version: channelResult.version };\n}\n\n// --- Semver comparison ---\n\n/**\n * Parse a version string into comparable numeric parts.\n * Handles formats like \"1.2.3\", \"1.2.3-beta.1\".\n * Returns null for unparseable strings.\n */\nfunction parseSemverParts(\n version: string,\n): { major: number; minor: number; patch: number; prerelease: string | null } | null {\n const match = /^v?(\\d+)\\.(\\d+)\\.(\\d+)(?:-(.+))?$/.exec(version.trim());\n if (!match) return null;\n return {\n major: parseInt(match[1], 10),\n minor: parseInt(match[2], 10),\n patch: parseInt(match[3], 10),\n prerelease: match[4] ?? null,\n };\n}\n\n/**\n * Compare two semver strings. Returns:\n * -1 if a < b, 0 if equal, 1 if a > b, null if either is unparseable.\n * Prerelease versions are considered older than the same version without prerelease.\n */\nexport function compareSemver(a: string | null, b: string | null): number | null {\n if (!a || !b) return null;\n const parsedA = parseSemverParts(a);\n const parsedB = parseSemverParts(b);\n if (!parsedA || !parsedB) return null;\n\n for (const field of ['major', 'minor', 'patch'] as const) {\n if (parsedA[field] !== parsedB[field]) {\n return parsedA[field] < parsedB[field] ? -1 : 1;\n }\n }\n\n // Both have same major.minor.patch — compare prerelease\n if (parsedA.prerelease === null && parsedB.prerelease === null) return 0;\n if (parsedA.prerelease !== null && parsedB.prerelease === null) return -1; // pre < release\n if (parsedA.prerelease === null && parsedB.prerelease !== null) return 1;\n\n // Both have prerelease — lexicographic fallback\n if (parsedA.prerelease! < parsedB.prerelease!) return -1;\n if (parsedA.prerelease! > parsedB.prerelease!) return 1;\n return 0;\n}\n\n// --- Install kind detection ---\n\n/**\n * Detect whether the current process is running from a git checkout or npm-installed package.\n * Checks for `.git` directory in the package root.\n */\nexport async function detectInstallKind(packageRoot: string): Promise<InstallKind> {\n try {\n await access(join(packageRoot, '.git'));\n return 'git';\n } catch {\n // No .git directory — likely installed via npm\n try {\n await access(join(packageRoot, 'package.json'));\n return 'package';\n } catch {\n return 'unknown';\n }\n }\n}\n\n/**\n * Resolve the xopc package root directory.\n * Walks up from this file to find package.json with name @xopcai/xopc.\n */\nexport async function resolvePackageRoot(): Promise<string | null> {\n let current = dirname(fileURLToPath(import.meta.url));\n for (let depth = 0; depth < 20; depth++) {\n const pkgPath = join(current, 'package.json');\n try {\n const raw = await readFile(pkgPath, 'utf-8');\n const parsed = JSON.parse(raw) as { name?: unknown };\n if (typeof parsed.name === 'string' && parsed.name === PACKAGE_NAME) {\n return current;\n }\n } catch {\n // continue\n }\n const parent = dirname(current);\n if (parent === current) return null;\n current = parent;\n }\n return null;\n}\n\n/** Get the current running version from package.json. */\nexport function getCurrentVersion(): string {\n return PACKAGE_VERSION;\n}\n"],"mappings":"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"}
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
import type { UpdateChannel } from './update-channels.js';
|
|
2
|
+
export type AutoUpdateResult = {
|
|
3
|
+
ok: boolean;
|
|
4
|
+
exitCode: number | null;
|
|
5
|
+
reason?: string;
|
|
6
|
+
stdout?: string;
|
|
7
|
+
stderr?: string;
|
|
8
|
+
};
|
|
9
|
+
/**
|
|
10
|
+
* Spawn a child process to execute `xopc update --yes --channel <channel> --json`.
|
|
11
|
+
* Uses the current runtime (process.execPath) and entry point (process.argv[1]) to
|
|
12
|
+
* ensure the correct Node.js version and binary path are used.
|
|
13
|
+
*/
|
|
14
|
+
export declare function runAutoUpdateCommand(params: {
|
|
15
|
+
channel: UpdateChannel;
|
|
16
|
+
root?: string | null;
|
|
17
|
+
timeoutMs?: number;
|
|
18
|
+
}): Promise<AutoUpdateResult>;
|
|
@@ -0,0 +1,112 @@
|
|
|
1
|
+
import { join } from "node:path";
|
|
2
|
+
import { access } from "node:fs/promises";
|
|
3
|
+
import { spawn } from "node:child_process";
|
|
4
|
+
//#region src/infra/update-runner.ts
|
|
5
|
+
const AUTO_UPDATE_TIMEOUT_MS = 2700 * 1e3;
|
|
6
|
+
/**
|
|
7
|
+
* Spawn a child process to execute `xopc update --yes --channel <channel> --json`.
|
|
8
|
+
* Uses the current runtime (process.execPath) and entry point (process.argv[1]) to
|
|
9
|
+
* ensure the correct Node.js version and binary path are used.
|
|
10
|
+
*/
|
|
11
|
+
async function runAutoUpdateCommand(params) {
|
|
12
|
+
const timeoutMs = params.timeoutMs ?? AUTO_UPDATE_TIMEOUT_MS;
|
|
13
|
+
const argv = await resolveUpdateCommandArgv([
|
|
14
|
+
"update",
|
|
15
|
+
"--yes",
|
|
16
|
+
"--channel",
|
|
17
|
+
params.channel,
|
|
18
|
+
"--json"
|
|
19
|
+
], params.root ?? null);
|
|
20
|
+
return new Promise((resolve) => {
|
|
21
|
+
const child = spawn(argv[0], argv.slice(1), {
|
|
22
|
+
env: {
|
|
23
|
+
...process.env,
|
|
24
|
+
XOPC_AUTO_UPDATE: "1"
|
|
25
|
+
},
|
|
26
|
+
stdio: [
|
|
27
|
+
"ignore",
|
|
28
|
+
"pipe",
|
|
29
|
+
"pipe"
|
|
30
|
+
],
|
|
31
|
+
detached: false
|
|
32
|
+
});
|
|
33
|
+
let stdout = "";
|
|
34
|
+
let stderr = "";
|
|
35
|
+
const timeoutId = setTimeout(() => {
|
|
36
|
+
child.kill("SIGTERM");
|
|
37
|
+
}, timeoutMs);
|
|
38
|
+
const finish = (result) => {
|
|
39
|
+
clearTimeout(timeoutId);
|
|
40
|
+
resolve(result);
|
|
41
|
+
};
|
|
42
|
+
child.stdout?.on("data", (chunk) => {
|
|
43
|
+
stdout += chunk.toString();
|
|
44
|
+
if (stdout.length > 64e3) stdout = stdout.slice(-32e3);
|
|
45
|
+
});
|
|
46
|
+
child.stderr?.on("data", (chunk) => {
|
|
47
|
+
stderr += chunk.toString();
|
|
48
|
+
if (stderr.length > 64e3) stderr = stderr.slice(-32e3);
|
|
49
|
+
});
|
|
50
|
+
child.on("error", (err) => {
|
|
51
|
+
finish({
|
|
52
|
+
ok: false,
|
|
53
|
+
exitCode: null,
|
|
54
|
+
reason: String(err),
|
|
55
|
+
stdout,
|
|
56
|
+
stderr
|
|
57
|
+
});
|
|
58
|
+
});
|
|
59
|
+
child.on("exit", (code, signal) => {
|
|
60
|
+
if (signal === "SIGTERM" || code === 143) {
|
|
61
|
+
finish({
|
|
62
|
+
ok: false,
|
|
63
|
+
exitCode: code,
|
|
64
|
+
reason: "timeout",
|
|
65
|
+
stdout,
|
|
66
|
+
stderr
|
|
67
|
+
});
|
|
68
|
+
return;
|
|
69
|
+
}
|
|
70
|
+
finish({
|
|
71
|
+
ok: code === 0,
|
|
72
|
+
exitCode: code,
|
|
73
|
+
reason: code === 0 ? void 0 : "non-zero-exit",
|
|
74
|
+
stdout,
|
|
75
|
+
stderr
|
|
76
|
+
});
|
|
77
|
+
});
|
|
78
|
+
});
|
|
79
|
+
}
|
|
80
|
+
/**
|
|
81
|
+
* Resolve the argv array for spawning the update command.
|
|
82
|
+
*
|
|
83
|
+
* Priority:
|
|
84
|
+
* 1. process.execPath + process.argv[1] (current runtime + entry point)
|
|
85
|
+
* 2. process.execPath + known dist entry points in root
|
|
86
|
+
* 3. Fallback to bare `xopc` (assumes global install)
|
|
87
|
+
*/
|
|
88
|
+
async function resolveUpdateCommandArgv(baseArgs, root) {
|
|
89
|
+
const execPath = process.execPath?.trim();
|
|
90
|
+
const argv1 = process.argv[1]?.trim();
|
|
91
|
+
if (execPath && argv1) return [
|
|
92
|
+
execPath,
|
|
93
|
+
argv1,
|
|
94
|
+
...baseArgs
|
|
95
|
+
];
|
|
96
|
+
if (execPath && root) {
|
|
97
|
+
const candidates = [join(root, "dist/src/cli/index.js"), join(root, "dist/index.js")];
|
|
98
|
+
for (const candidate of candidates) try {
|
|
99
|
+
await access(candidate);
|
|
100
|
+
return [
|
|
101
|
+
execPath,
|
|
102
|
+
candidate,
|
|
103
|
+
...baseArgs
|
|
104
|
+
];
|
|
105
|
+
} catch {}
|
|
106
|
+
}
|
|
107
|
+
return ["xopc", ...baseArgs];
|
|
108
|
+
}
|
|
109
|
+
//#endregion
|
|
110
|
+
export { runAutoUpdateCommand };
|
|
111
|
+
|
|
112
|
+
//# sourceMappingURL=update-runner.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"update-runner.js","names":[],"sources":["../../../src/infra/update-runner.ts"],"sourcesContent":["// src/infra/update-runner.ts\n\nimport { spawn } from 'node:child_process';\nimport { access } from 'node:fs/promises';\nimport { join } from 'node:path';\n\nimport type { UpdateChannel } from './update-channels.js';\n\nconst AUTO_UPDATE_TIMEOUT_MS = 45 * 60 * 1000; // 45 minutes\n\nexport type AutoUpdateResult = {\n ok: boolean;\n exitCode: number | null;\n reason?: string;\n stdout?: string;\n stderr?: string;\n};\n\n/**\n * Spawn a child process to execute `xopc update --yes --channel <channel> --json`.\n * Uses the current runtime (process.execPath) and entry point (process.argv[1]) to\n * ensure the correct Node.js version and binary path are used.\n */\nexport async function runAutoUpdateCommand(params: {\n channel: UpdateChannel;\n root?: string | null;\n timeoutMs?: number;\n}): Promise<AutoUpdateResult> {\n const timeoutMs = params.timeoutMs ?? AUTO_UPDATE_TIMEOUT_MS;\n const baseArgs = ['update', '--yes', '--channel', params.channel, '--json'];\n\n const argv = await resolveUpdateCommandArgv(baseArgs, params.root ?? null);\n\n return new Promise<AutoUpdateResult>((resolve) => {\n const child = spawn(argv[0], argv.slice(1), {\n env: {\n ...process.env,\n XOPC_AUTO_UPDATE: '1',\n },\n stdio: ['ignore', 'pipe', 'pipe'],\n detached: false,\n });\n\n let stdout = '';\n let stderr = '';\n\n const timeoutId = setTimeout(() => {\n child.kill('SIGTERM');\n }, timeoutMs);\n\n const finish = (result: AutoUpdateResult) => {\n clearTimeout(timeoutId);\n resolve(result);\n };\n\n child.stdout?.on('data', (chunk: Buffer) => {\n stdout += chunk.toString();\n if (stdout.length > 64_000) stdout = stdout.slice(-32_000);\n });\n child.stderr?.on('data', (chunk: Buffer) => {\n stderr += chunk.toString();\n if (stderr.length > 64_000) stderr = stderr.slice(-32_000);\n });\n\n child.on('error', (err) => {\n finish({ ok: false, exitCode: null, reason: String(err), stdout, stderr });\n });\n\n child.on('exit', (code, signal) => {\n if (signal === 'SIGTERM' || code === 143) {\n finish({ ok: false, exitCode: code, reason: 'timeout', stdout, stderr });\n return;\n }\n finish({\n ok: code === 0,\n exitCode: code,\n reason: code === 0 ? undefined : 'non-zero-exit',\n stdout,\n stderr,\n });\n });\n });\n}\n\n/**\n * Resolve the argv array for spawning the update command.\n *\n * Priority:\n * 1. process.execPath + process.argv[1] (current runtime + entry point)\n * 2. process.execPath + known dist entry points in root\n * 3. Fallback to bare `xopc` (assumes global install)\n */\nasync function resolveUpdateCommandArgv(\n baseArgs: string[],\n root: string | null,\n): Promise<string[]> {\n const execPath = process.execPath?.trim();\n const argv1 = process.argv[1]?.trim();\n\n // Best case: we know both the runtime and the entry point\n if (execPath && argv1) {\n return [execPath, argv1, ...baseArgs];\n }\n\n // Try known entry points in the package root\n if (execPath && root) {\n const candidates = [join(root, 'dist/src/cli/index.js'), join(root, 'dist/index.js')];\n for (const candidate of candidates) {\n try {\n await access(candidate);\n return [execPath, candidate, ...baseArgs];\n } catch {\n // try next\n }\n }\n }\n\n // Fallback: rely on global PATH\n return ['xopc', ...baseArgs];\n}\n"],"mappings":";;;;AAQA,MAAM,yBAAyB,OAAU;;;;;;AAezC,eAAsB,qBAAqB,QAIb;CAC5B,MAAM,YAAY,OAAO,aAAa;CAGtC,MAAM,OAAO,MAAM,yBAFF;EAAC;EAAU;EAAS;EAAa,OAAO;EAAS;EAAS,EAErB,OAAO,QAAQ,KAAK;AAE1E,QAAO,IAAI,SAA2B,YAAY;EAChD,MAAM,QAAQ,MAAM,KAAK,IAAI,KAAK,MAAM,EAAE,EAAE;GAC1C,KAAK;IACH,GAAG,QAAQ;IACX,kBAAkB;IACnB;GACD,OAAO;IAAC;IAAU;IAAQ;IAAO;GACjC,UAAU;GACX,CAAC;EAEF,IAAI,SAAS;EACb,IAAI,SAAS;EAEb,MAAM,YAAY,iBAAiB;AACjC,SAAM,KAAK,UAAU;KACpB,UAAU;EAEb,MAAM,UAAU,WAA6B;AAC3C,gBAAa,UAAU;AACvB,WAAQ,OAAO;;AAGjB,QAAM,QAAQ,GAAG,SAAS,UAAkB;AAC1C,aAAU,MAAM,UAAU;AAC1B,OAAI,OAAO,SAAS,KAAQ,UAAS,OAAO,MAAM,MAAQ;IAC1D;AACF,QAAM,QAAQ,GAAG,SAAS,UAAkB;AAC1C,aAAU,MAAM,UAAU;AAC1B,OAAI,OAAO,SAAS,KAAQ,UAAS,OAAO,MAAM,MAAQ;IAC1D;AAEF,QAAM,GAAG,UAAU,QAAQ;AACzB,UAAO;IAAE,IAAI;IAAO,UAAU;IAAM,QAAQ,OAAO,IAAI;IAAE;IAAQ;IAAQ,CAAC;IAC1E;AAEF,QAAM,GAAG,SAAS,MAAM,WAAW;AACjC,OAAI,WAAW,aAAa,SAAS,KAAK;AACxC,WAAO;KAAE,IAAI;KAAO,UAAU;KAAM,QAAQ;KAAW;KAAQ;KAAQ,CAAC;AACxE;;AAEF,UAAO;IACL,IAAI,SAAS;IACb,UAAU;IACV,QAAQ,SAAS,IAAI,KAAA,IAAY;IACjC;IACA;IACD,CAAC;IACF;GACF;;;;;;;;;;AAWJ,eAAe,yBACb,UACA,MACmB;CACnB,MAAM,WAAW,QAAQ,UAAU,MAAM;CACzC,MAAM,QAAQ,QAAQ,KAAK,IAAI,MAAM;AAGrC,KAAI,YAAY,MACd,QAAO;EAAC;EAAU;EAAO,GAAG;EAAS;AAIvC,KAAI,YAAY,MAAM;EACpB,MAAM,aAAa,CAAC,KAAK,MAAM,wBAAwB,EAAE,KAAK,MAAM,gBAAgB,CAAC;AACrF,OAAK,MAAM,aAAa,WACtB,KAAI;AACF,SAAM,OAAO,UAAU;AACvB,UAAO;IAAC;IAAU;IAAW,GAAG;IAAS;UACnC;;AAOZ,QAAO,CAAC,QAAQ,GAAG,SAAS"}
|