@zhijiewang/openharness 2.20.0 → 2.22.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/README.md +21 -1
- package/README.zh-CN.md +21 -1
- package/dist/commands/ai.js +10 -0
- package/dist/commands/session.d.ts +18 -1
- package/dist/commands/session.js +82 -2
- package/dist/commands/settings.d.ts +1 -1
- package/dist/commands/settings.js +71 -1
- package/dist/harness/api-key-helper.d.ts +32 -0
- package/dist/harness/api-key-helper.js +70 -0
- package/dist/harness/config.d.ts +38 -0
- package/dist/harness/credentials.d.ts +6 -4
- package/dist/harness/credentials.js +15 -4
- package/dist/harness/hooks.d.ts +22 -1
- package/dist/harness/hooks.js +37 -0
- package/dist/main.js +361 -108
- package/dist/mcp/elicitation.d.ts +66 -0
- package/dist/mcp/elicitation.js +88 -0
- package/dist/mcp/loader.d.ts +29 -2
- package/dist/mcp/loader.js +59 -3
- package/dist/mcp/roots.d.ts +36 -0
- package/dist/mcp/roots.js +56 -0
- package/dist/mcp/transport.js +45 -3
- package/dist/providers/index.d.ts +25 -1
- package/dist/providers/index.js +27 -2
- package/dist/query/index.js +1 -1
- package/dist/query/tools.d.ts +2 -2
- package/dist/query/tools.js +68 -4
- package/dist/query/types.d.ts +10 -0
- package/dist/tools/EnterWorktreeTool/index.js +4 -0
- package/dist/tools/ExitWorktreeTool/index.js +7 -0
- package/dist/utils/debug.d.ts +63 -0
- package/dist/utils/debug.js +122 -0
- package/dist/utils/install-method.d.ts +42 -0
- package/dist/utils/install-method.js +110 -0
- package/package.json +1 -1
package/dist/query/types.d.ts
CHANGED
|
@@ -22,6 +22,16 @@ export type QueryConfig = {
|
|
|
22
22
|
gitCommitPerTool?: boolean;
|
|
23
23
|
/** For sub-agent invocations: the agent role name (feeds into the model router). */
|
|
24
24
|
role?: string;
|
|
25
|
+
/**
|
|
26
|
+
* MCP tool name (e.g. `mcp__myperm__check`) consulted when a tool needs
|
|
27
|
+
* approval and no permission hook gave a decision (audit B1). Mirrors
|
|
28
|
+
* Claude Code's `--permission-prompt-tool`. The tool is invoked with
|
|
29
|
+
* `{ tool_name, input }` and is expected to return a JSON string with
|
|
30
|
+
* shape `{ "behavior": "allow" | "deny", "message"?: string }`. Falls
|
|
31
|
+
* through to the interactive `askUser` prompt (or headless deny) when
|
|
32
|
+
* the tool is missing, throws, or returns malformed JSON.
|
|
33
|
+
*/
|
|
34
|
+
permissionPromptTool?: string;
|
|
25
35
|
};
|
|
26
36
|
export type TransitionReason = "next_turn" | "retry_network" | "retry_prompt_too_long" | "retry_max_output_tokens";
|
|
27
37
|
export type QueryLoopState = {
|
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import { z } from "zod";
|
|
2
2
|
import { createWorktree, isGitRepo } from "../../git/index.js";
|
|
3
|
+
import { emitHook } from "../../harness/hooks.js";
|
|
3
4
|
const inputSchema = z.object({
|
|
4
5
|
branch: z.string().optional().describe("Branch name for the worktree (auto-generated if omitted)"),
|
|
5
6
|
});
|
|
@@ -22,6 +23,9 @@ export const EnterWorktreeTool = {
|
|
|
22
23
|
if (!path) {
|
|
23
24
|
return { output: "Failed to create worktree.", isError: true };
|
|
24
25
|
}
|
|
26
|
+
// Symmetric to taskCreated — fire only on the success path so audit hooks
|
|
27
|
+
// can react to the new worktree (e.g. set up a per-worktree scratch dir).
|
|
28
|
+
emitHook("worktreeCreate", { worktreePath: path, worktreeParent: context.workingDir });
|
|
25
29
|
return { output: `Worktree created at: ${path}\nUse ExitWorktree to clean up when done.`, isError: false };
|
|
26
30
|
},
|
|
27
31
|
prompt() {
|
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import { z } from "zod";
|
|
2
2
|
import { hasWorktreeChanges, removeWorktree } from "../../git/index.js";
|
|
3
|
+
import { emitHook } from "../../harness/hooks.js";
|
|
3
4
|
const inputSchema = z.object({
|
|
4
5
|
path: z.string().describe("Path to the worktree to remove"),
|
|
5
6
|
force: z.boolean().optional().describe("Force removal even with uncommitted changes"),
|
|
@@ -24,6 +25,12 @@ export const ExitWorktreeTool = {
|
|
|
24
25
|
}
|
|
25
26
|
try {
|
|
26
27
|
removeWorktree(input.path);
|
|
28
|
+
// Fire after removeWorktree resolves so the hook only sees confirmed
|
|
29
|
+
// removals — symmetric to worktreeCreate firing on success.
|
|
30
|
+
emitHook("worktreeRemove", {
|
|
31
|
+
worktreePath: input.path,
|
|
32
|
+
worktreeForced: input.force ? "true" : "false",
|
|
33
|
+
});
|
|
27
34
|
return { output: `Worktree removed: ${input.path}`, isError: false };
|
|
28
35
|
}
|
|
29
36
|
catch (err) {
|
|
@@ -0,0 +1,63 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Categorized debug logger — gates verbose internal traces behind a runtime
|
|
3
|
+
* switch so they're silent by default but easy to flip on for support / CI.
|
|
4
|
+
*
|
|
5
|
+
* Activation precedence (highest first):
|
|
6
|
+
* 1. `configureDebug({ categories })` from a CLI flag (`--debug [cats]`)
|
|
7
|
+
* 2. `OH_DEBUG` env var
|
|
8
|
+
*
|
|
9
|
+
* Sink precedence:
|
|
10
|
+
* 1. `configureDebug({ file })` from `--debug-file <path>`
|
|
11
|
+
* 2. `OH_DEBUG_FILE` env var
|
|
12
|
+
* 3. `process.stderr` (default)
|
|
13
|
+
*
|
|
14
|
+
* Categories are arbitrary strings — call sites pick them. The CLI accepts a
|
|
15
|
+
* comma-separated list (`--debug mcp,hooks`) or `--debug` alone for "all".
|
|
16
|
+
*
|
|
17
|
+
* Wire pattern:
|
|
18
|
+
* import { configureDebug, debug } from "./utils/debug.js";
|
|
19
|
+
* configureDebug({ categories: opts.debug, file: opts.debugFile });
|
|
20
|
+
* debug("mcp", "connected", server.name);
|
|
21
|
+
*/
|
|
22
|
+
/**
|
|
23
|
+
* Parse the raw flag value into a Set of enabled categories.
|
|
24
|
+
*
|
|
25
|
+
* Accepted values:
|
|
26
|
+
* - `undefined` / empty / `false` → no debug
|
|
27
|
+
* - `true` / `"*"` / `"all"` / `"1"` → all categories
|
|
28
|
+
* - `"mcp,hooks,provider"` → comma-separated explicit list
|
|
29
|
+
*
|
|
30
|
+
* Whitespace is trimmed and empty entries dropped, so `"mcp, ,hooks"` is
|
|
31
|
+
* equivalent to `"mcp,hooks"`. Pure function — exposed for testability.
|
|
32
|
+
*/
|
|
33
|
+
export declare function parseDebugCategories(raw: string | boolean | undefined): Set<string>;
|
|
34
|
+
export interface ConfigureDebugOptions {
|
|
35
|
+
/** CLI flag value: `--debug` → true, `--debug mcp` → "mcp", absent → undefined. */
|
|
36
|
+
categories?: string | boolean | undefined;
|
|
37
|
+
/** CLI flag value: `--debug-file <path>` — appended to, never truncated. */
|
|
38
|
+
file?: string;
|
|
39
|
+
/** Test injection — overrides the file/stderr sink. Not used at runtime. */
|
|
40
|
+
sink?: NodeJS.WritableStream;
|
|
41
|
+
}
|
|
42
|
+
/**
|
|
43
|
+
* Apply debug configuration. Safe to call multiple times — later calls fully
|
|
44
|
+
* replace earlier state. When `categories` is undefined, falls back to
|
|
45
|
+
* `OH_DEBUG`; when `file` is undefined, falls back to `OH_DEBUG_FILE`.
|
|
46
|
+
*
|
|
47
|
+
* File output uses `appendFileSync` rather than a `WriteStream` so each
|
|
48
|
+
* `debug()` line lands on disk before the function returns. That trades a
|
|
49
|
+
* little throughput for ordering guarantees that matter when debugging
|
|
50
|
+
* crashes — a streamed sink could lose its tail buffer on `process.exit`.
|
|
51
|
+
*/
|
|
52
|
+
export declare function configureDebug(opts?: ConfigureDebugOptions): void;
|
|
53
|
+
/** Whether the given category is currently emitting. Cheap — a Set lookup. */
|
|
54
|
+
export declare function isDebugEnabled(category: string): boolean;
|
|
55
|
+
/**
|
|
56
|
+
* Emit a debug line for the given category. Cheap no-op when the category is
|
|
57
|
+
* disabled — argument formatting is skipped entirely. Each line is prefixed
|
|
58
|
+
* with `[debug:<cat>] +<elapsed_ms>ms` so categories interleave readably.
|
|
59
|
+
*/
|
|
60
|
+
export declare function debug(category: string, ...args: unknown[]): void;
|
|
61
|
+
/** @internal Test-only: reset module-level state between cases. */
|
|
62
|
+
export declare function _resetDebugForTest(): void;
|
|
63
|
+
//# sourceMappingURL=debug.d.ts.map
|
|
@@ -0,0 +1,122 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Categorized debug logger — gates verbose internal traces behind a runtime
|
|
3
|
+
* switch so they're silent by default but easy to flip on for support / CI.
|
|
4
|
+
*
|
|
5
|
+
* Activation precedence (highest first):
|
|
6
|
+
* 1. `configureDebug({ categories })` from a CLI flag (`--debug [cats]`)
|
|
7
|
+
* 2. `OH_DEBUG` env var
|
|
8
|
+
*
|
|
9
|
+
* Sink precedence:
|
|
10
|
+
* 1. `configureDebug({ file })` from `--debug-file <path>`
|
|
11
|
+
* 2. `OH_DEBUG_FILE` env var
|
|
12
|
+
* 3. `process.stderr` (default)
|
|
13
|
+
*
|
|
14
|
+
* Categories are arbitrary strings — call sites pick them. The CLI accepts a
|
|
15
|
+
* comma-separated list (`--debug mcp,hooks`) or `--debug` alone for "all".
|
|
16
|
+
*
|
|
17
|
+
* Wire pattern:
|
|
18
|
+
* import { configureDebug, debug } from "./utils/debug.js";
|
|
19
|
+
* configureDebug({ categories: opts.debug, file: opts.debugFile });
|
|
20
|
+
* debug("mcp", "connected", server.name);
|
|
21
|
+
*/
|
|
22
|
+
import { appendFileSync } from "node:fs";
|
|
23
|
+
const ALL = "*";
|
|
24
|
+
let enabledCategories = new Set();
|
|
25
|
+
let debugFilePath;
|
|
26
|
+
let sinkOverride;
|
|
27
|
+
let started = Date.now();
|
|
28
|
+
/**
|
|
29
|
+
* Parse the raw flag value into a Set of enabled categories.
|
|
30
|
+
*
|
|
31
|
+
* Accepted values:
|
|
32
|
+
* - `undefined` / empty / `false` → no debug
|
|
33
|
+
* - `true` / `"*"` / `"all"` / `"1"` → all categories
|
|
34
|
+
* - `"mcp,hooks,provider"` → comma-separated explicit list
|
|
35
|
+
*
|
|
36
|
+
* Whitespace is trimmed and empty entries dropped, so `"mcp, ,hooks"` is
|
|
37
|
+
* equivalent to `"mcp,hooks"`. Pure function — exposed for testability.
|
|
38
|
+
*/
|
|
39
|
+
export function parseDebugCategories(raw) {
|
|
40
|
+
if (raw === undefined || raw === false || raw === "")
|
|
41
|
+
return new Set();
|
|
42
|
+
if (raw === true)
|
|
43
|
+
return new Set([ALL]);
|
|
44
|
+
const lower = raw.toLowerCase();
|
|
45
|
+
if (lower === "*" || lower === "all" || lower === "true" || lower === "1")
|
|
46
|
+
return new Set([ALL]);
|
|
47
|
+
return new Set(raw
|
|
48
|
+
.split(",")
|
|
49
|
+
.map((s) => s.trim())
|
|
50
|
+
.filter(Boolean));
|
|
51
|
+
}
|
|
52
|
+
/**
|
|
53
|
+
* Apply debug configuration. Safe to call multiple times — later calls fully
|
|
54
|
+
* replace earlier state. When `categories` is undefined, falls back to
|
|
55
|
+
* `OH_DEBUG`; when `file` is undefined, falls back to `OH_DEBUG_FILE`.
|
|
56
|
+
*
|
|
57
|
+
* File output uses `appendFileSync` rather than a `WriteStream` so each
|
|
58
|
+
* `debug()` line lands on disk before the function returns. That trades a
|
|
59
|
+
* little throughput for ordering guarantees that matter when debugging
|
|
60
|
+
* crashes — a streamed sink could lose its tail buffer on `process.exit`.
|
|
61
|
+
*/
|
|
62
|
+
export function configureDebug(opts = {}) {
|
|
63
|
+
const rawCats = opts.categories !== undefined ? opts.categories : process.env.OH_DEBUG;
|
|
64
|
+
enabledCategories = parseDebugCategories(rawCats);
|
|
65
|
+
sinkOverride = opts.sink;
|
|
66
|
+
debugFilePath = opts.sink ? undefined : (opts.file ?? process.env.OH_DEBUG_FILE);
|
|
67
|
+
started = Date.now();
|
|
68
|
+
}
|
|
69
|
+
/** Whether the given category is currently emitting. Cheap — a Set lookup. */
|
|
70
|
+
export function isDebugEnabled(category) {
|
|
71
|
+
return enabledCategories.has(ALL) || enabledCategories.has(category);
|
|
72
|
+
}
|
|
73
|
+
/**
|
|
74
|
+
* Emit a debug line for the given category. Cheap no-op when the category is
|
|
75
|
+
* disabled — argument formatting is skipped entirely. Each line is prefixed
|
|
76
|
+
* with `[debug:<cat>] +<elapsed_ms>ms` so categories interleave readably.
|
|
77
|
+
*/
|
|
78
|
+
export function debug(category, ...args) {
|
|
79
|
+
if (!isDebugEnabled(category))
|
|
80
|
+
return;
|
|
81
|
+
const elapsed = Date.now() - started;
|
|
82
|
+
const formatted = args
|
|
83
|
+
.map((a) => {
|
|
84
|
+
if (typeof a === "string")
|
|
85
|
+
return a;
|
|
86
|
+
if (a instanceof Error)
|
|
87
|
+
return a.stack ?? a.message;
|
|
88
|
+
try {
|
|
89
|
+
return JSON.stringify(a);
|
|
90
|
+
}
|
|
91
|
+
catch {
|
|
92
|
+
return String(a);
|
|
93
|
+
}
|
|
94
|
+
})
|
|
95
|
+
.join(" ");
|
|
96
|
+
const line = `[debug:${category}] +${elapsed}ms ${formatted}\n`;
|
|
97
|
+
if (sinkOverride) {
|
|
98
|
+
sinkOverride.write(line);
|
|
99
|
+
}
|
|
100
|
+
else if (debugFilePath) {
|
|
101
|
+
try {
|
|
102
|
+
appendFileSync(debugFilePath, line);
|
|
103
|
+
}
|
|
104
|
+
catch (err) {
|
|
105
|
+
// Fall back to stderr so a broken --debug-file doesn't swallow output.
|
|
106
|
+
process.stderr.write(`[debug] could not append to '${debugFilePath}': ${err instanceof Error ? err.message : String(err)}\n`);
|
|
107
|
+
process.stderr.write(line);
|
|
108
|
+
debugFilePath = undefined;
|
|
109
|
+
}
|
|
110
|
+
}
|
|
111
|
+
else {
|
|
112
|
+
process.stderr.write(line);
|
|
113
|
+
}
|
|
114
|
+
}
|
|
115
|
+
/** @internal Test-only: reset module-level state between cases. */
|
|
116
|
+
export function _resetDebugForTest() {
|
|
117
|
+
enabledCategories = new Set();
|
|
118
|
+
debugFilePath = undefined;
|
|
119
|
+
sinkOverride = undefined;
|
|
120
|
+
started = Date.now();
|
|
121
|
+
}
|
|
122
|
+
//# sourceMappingURL=debug.js.map
|
|
@@ -0,0 +1,42 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Detect how the running OH CLI was installed (audit B7) so `oh update` can
|
|
3
|
+
* print the appropriate upgrade command. Pure function — `detectInstallMethod`
|
|
4
|
+
* inspects the process's own filesystem path and current working directory;
|
|
5
|
+
* exported for unit testing and reuse.
|
|
6
|
+
*
|
|
7
|
+
* Detection rules, in order:
|
|
8
|
+
* - "local-clone" → `dist/main.js` lives inside a git repo whose root is
|
|
9
|
+
* the package itself (the user is running from a clone).
|
|
10
|
+
* Suggest `git pull && npm install && npm run build`.
|
|
11
|
+
* - "npm-global" → `dist/main.js` lives under a directory containing the
|
|
12
|
+
* segment `node_modules/@zhijiewang/openharness/`. This
|
|
13
|
+
* is the standard npm global install layout. Suggest
|
|
14
|
+
* `npm install -g @zhijiewang/openharness@latest`.
|
|
15
|
+
* - "npx-cache" → `dist/main.js` lives under a path containing
|
|
16
|
+
* `_npx/` (npx caches packages there). npx auto-fetches
|
|
17
|
+
* the latest by default; suggest re-running with
|
|
18
|
+
* `@latest` to bypass cache.
|
|
19
|
+
* - "unknown" → Couldn't classify. Print all three options and let
|
|
20
|
+
* the user choose.
|
|
21
|
+
*/
|
|
22
|
+
export type InstallMethod = "local-clone" | "npm-global" | "npx-cache" | "unknown";
|
|
23
|
+
export interface InstallMethodResult {
|
|
24
|
+
method: InstallMethod;
|
|
25
|
+
/** The detected install root, mostly for diagnostics. */
|
|
26
|
+
root: string;
|
|
27
|
+
/** Multi-line user-facing message describing the upgrade command. */
|
|
28
|
+
message: string;
|
|
29
|
+
}
|
|
30
|
+
/**
|
|
31
|
+
* Classify the install method given the running script's filesystem path.
|
|
32
|
+
* `mainPath` defaults to `import.meta.url`-derived path in the CLI; tests
|
|
33
|
+
* override it.
|
|
34
|
+
*/
|
|
35
|
+
export declare function detectInstallMethod(mainPath: string): InstallMethodResult;
|
|
36
|
+
/**
|
|
37
|
+
* Default `mainPath` resolver — walks up from `process.argv[1]` to find the
|
|
38
|
+
* package root. Exported so tests can stub it. Falls back to argv[1] verbatim
|
|
39
|
+
* when nothing matches.
|
|
40
|
+
*/
|
|
41
|
+
export declare function getDefaultMainPath(): string;
|
|
42
|
+
//# sourceMappingURL=install-method.d.ts.map
|
|
@@ -0,0 +1,110 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Detect how the running OH CLI was installed (audit B7) so `oh update` can
|
|
3
|
+
* print the appropriate upgrade command. Pure function — `detectInstallMethod`
|
|
4
|
+
* inspects the process's own filesystem path and current working directory;
|
|
5
|
+
* exported for unit testing and reuse.
|
|
6
|
+
*
|
|
7
|
+
* Detection rules, in order:
|
|
8
|
+
* - "local-clone" → `dist/main.js` lives inside a git repo whose root is
|
|
9
|
+
* the package itself (the user is running from a clone).
|
|
10
|
+
* Suggest `git pull && npm install && npm run build`.
|
|
11
|
+
* - "npm-global" → `dist/main.js` lives under a directory containing the
|
|
12
|
+
* segment `node_modules/@zhijiewang/openharness/`. This
|
|
13
|
+
* is the standard npm global install layout. Suggest
|
|
14
|
+
* `npm install -g @zhijiewang/openharness@latest`.
|
|
15
|
+
* - "npx-cache" → `dist/main.js` lives under a path containing
|
|
16
|
+
* `_npx/` (npx caches packages there). npx auto-fetches
|
|
17
|
+
* the latest by default; suggest re-running with
|
|
18
|
+
* `@latest` to bypass cache.
|
|
19
|
+
* - "unknown" → Couldn't classify. Print all three options and let
|
|
20
|
+
* the user choose.
|
|
21
|
+
*/
|
|
22
|
+
import { existsSync } from "node:fs";
|
|
23
|
+
import { dirname, join, sep } from "node:path";
|
|
24
|
+
/**
|
|
25
|
+
* Classify the install method given the running script's filesystem path.
|
|
26
|
+
* `mainPath` defaults to `import.meta.url`-derived path in the CLI; tests
|
|
27
|
+
* override it.
|
|
28
|
+
*/
|
|
29
|
+
export function detectInstallMethod(mainPath) {
|
|
30
|
+
// Normalize to forward slashes so the substring tests below work on Windows.
|
|
31
|
+
const normalized = mainPath.replace(/\\/g, "/");
|
|
32
|
+
// npx-cache: path contains `/_npx/` (Node's npx puts packages there)
|
|
33
|
+
if (normalized.includes("/_npx/")) {
|
|
34
|
+
return {
|
|
35
|
+
method: "npx-cache",
|
|
36
|
+
root: dirname(mainPath),
|
|
37
|
+
message: [
|
|
38
|
+
"You're running OH via npx (auto-fetched on each invocation).",
|
|
39
|
+
"To force the latest version on the next run, use:",
|
|
40
|
+
"",
|
|
41
|
+
" npx @zhijiewang/openharness@latest",
|
|
42
|
+
"",
|
|
43
|
+
"Or install globally to avoid the npx cache entirely:",
|
|
44
|
+
" npm install -g @zhijiewang/openharness@latest",
|
|
45
|
+
].join("\n"),
|
|
46
|
+
};
|
|
47
|
+
}
|
|
48
|
+
// local-clone: walk up to find a package.json whose name matches AND a .git dir
|
|
49
|
+
let dir = dirname(mainPath);
|
|
50
|
+
while (dir && dir !== dirname(dir)) {
|
|
51
|
+
const pkgPath = join(dir, "package.json");
|
|
52
|
+
if (existsSync(pkgPath)) {
|
|
53
|
+
const isClone = existsSync(join(dir, ".git"));
|
|
54
|
+
if (isClone) {
|
|
55
|
+
return {
|
|
56
|
+
method: "local-clone",
|
|
57
|
+
root: dir,
|
|
58
|
+
message: [
|
|
59
|
+
`Detected a local clone at: ${dir}`,
|
|
60
|
+
"Pull the latest and rebuild:",
|
|
61
|
+
"",
|
|
62
|
+
` cd ${dir}`,
|
|
63
|
+
" git pull && npm install && npm run build",
|
|
64
|
+
].join("\n"),
|
|
65
|
+
};
|
|
66
|
+
}
|
|
67
|
+
// npm-global: the package.json belongs to OH and lives under a global
|
|
68
|
+
// node_modules directory.
|
|
69
|
+
if (normalized.includes("/node_modules/@zhijiewang/openharness/")) {
|
|
70
|
+
return {
|
|
71
|
+
method: "npm-global",
|
|
72
|
+
root: dir,
|
|
73
|
+
message: [
|
|
74
|
+
`Detected a global npm install at: ${dir}`,
|
|
75
|
+
"Upgrade with:",
|
|
76
|
+
"",
|
|
77
|
+
" npm install -g @zhijiewang/openharness@latest",
|
|
78
|
+
].join("\n"),
|
|
79
|
+
};
|
|
80
|
+
}
|
|
81
|
+
break;
|
|
82
|
+
}
|
|
83
|
+
dir = dirname(dir);
|
|
84
|
+
}
|
|
85
|
+
return {
|
|
86
|
+
method: "unknown",
|
|
87
|
+
root: dirname(mainPath),
|
|
88
|
+
message: [
|
|
89
|
+
"Could not determine how OH was installed. Pick the option that matches your setup:",
|
|
90
|
+
"",
|
|
91
|
+
" Global npm install: npm install -g @zhijiewang/openharness@latest",
|
|
92
|
+
" npx (one-shot): npx @zhijiewang/openharness@latest",
|
|
93
|
+
" Local clone: git pull && npm install && npm run build",
|
|
94
|
+
].join("\n"),
|
|
95
|
+
};
|
|
96
|
+
}
|
|
97
|
+
/**
|
|
98
|
+
* Default `mainPath` resolver — walks up from `process.argv[1]` to find the
|
|
99
|
+
* package root. Exported so tests can stub it. Falls back to argv[1] verbatim
|
|
100
|
+
* when nothing matches.
|
|
101
|
+
*/
|
|
102
|
+
export function getDefaultMainPath() {
|
|
103
|
+
const entry = process.argv[1] ?? "";
|
|
104
|
+
if (!entry)
|
|
105
|
+
return "";
|
|
106
|
+
// If argv[1] points at a `dist/main.js`, that's already the right anchor.
|
|
107
|
+
// Otherwise return as-is and let `detectInstallMethod` figure it out.
|
|
108
|
+
return entry.split(sep).join("/");
|
|
109
|
+
}
|
|
110
|
+
//# sourceMappingURL=install-method.js.map
|