@skj1724/oh-my-opencode 3.19.3 → 3.19.5

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.
@@ -4,9 +4,13 @@
4
4
 
5
5
  import { spawnSync } from "node:child_process";
6
6
  import { createRequire } from "node:module";
7
+ import { dirname, join } from "node:path";
8
+ import { fileURLToPath } from "node:url";
7
9
  import { getPlatformPackage, getBinaryPath } from "./platform.js";
8
10
 
9
11
  const require = createRequire(import.meta.url);
12
+ const __dirname = dirname(fileURLToPath(import.meta.url));
13
+ const distCliPath = join(__dirname, "..", "dist", "cli", "index.js");
10
14
 
11
15
  /**
12
16
  * Detect libc family on Linux
@@ -29,52 +33,55 @@ function getLibcFamily() {
29
33
  function main() {
30
34
  const { platform, arch } = process;
31
35
  const libcFamily = getLibcFamily();
36
+ const args = process.argv.slice(2);
32
37
 
33
- // Get platform package name
34
- let pkg;
35
- try {
36
- pkg = getPlatformPackage({ platform, arch, libcFamily });
37
- } catch (error) {
38
- console.error(`\noh-my-opencode: ${error.message}\n`);
39
- process.exit(1);
40
- }
41
-
42
- // Resolve binary path
43
- const binRelPath = getBinaryPath(pkg, platform);
44
-
45
- let binPath;
38
+ let binPath = null;
46
39
  try {
40
+ const pkg = getPlatformPackage({ platform, arch, libcFamily });
41
+ const binRelPath = getBinaryPath(pkg, platform);
47
42
  binPath = require.resolve(binRelPath);
48
43
  } catch {
49
- console.error(`\noh-my-opencode: Platform binary not installed.`);
50
- console.error(`\nYour platform: ${platform}-${arch}${libcFamily === "musl" ? "-musl" : ""}`);
51
- console.error(`Expected package: ${pkg}`);
52
- console.error(`\nTo fix, run:`);
53
- console.error(` npm install ${pkg}\n`);
54
- process.exit(1);
55
44
  }
56
45
 
57
- // Spawn the binary
58
- const result = spawnSync(binPath, process.argv.slice(2), {
46
+ if (binPath) {
47
+ const result = spawnSync(binPath, args, {
48
+ stdio: "inherit",
49
+ });
50
+
51
+ if (!result.error && !result.signal) {
52
+ process.exit(result.status ?? 0);
53
+ }
54
+
55
+ if (result.error) {
56
+ console.warn(`\noh-my-opencode: Platform binary failed: ${result.error.message}`);
57
+ } else if (result.signal) {
58
+ console.warn(`\noh-my-opencode: Platform binary crashed (signal: ${result.signal})`);
59
+ }
60
+ console.warn(" Falling back to Bun...\n");
61
+ }
62
+
63
+ const fallbackResult = spawnSync("bun", ["run", distCliPath, ...args], {
59
64
  stdio: "inherit",
60
65
  });
61
66
 
62
- // Handle spawn errors
63
- if (result.error) {
64
- console.error(`\noh-my-opencode: Failed to execute binary.`);
65
- console.error(`Error: ${result.error.message}\n`);
67
+ if (fallbackResult.error) {
68
+ console.error(`\noh-my-opencode: Failed to execute CLI via Bun.`);
69
+ if (fallbackResult.error.code === "ENOENT") {
70
+ console.error(` Bun is not installed. Install it from https://bun.sh\n`);
71
+ } else {
72
+ console.error(` Error: ${fallbackResult.error.message}\n`);
73
+ }
66
74
  process.exit(2);
67
75
  }
68
76
 
69
- // Handle signals
70
- if (result.signal) {
71
- const signalNum = result.signal === "SIGTERM" ? 15 :
72
- result.signal === "SIGKILL" ? 9 :
73
- result.signal === "SIGINT" ? 2 : 1;
77
+ if (fallbackResult.signal) {
78
+ const signalNum = fallbackResult.signal === "SIGTERM" ? 15 :
79
+ fallbackResult.signal === "SIGKILL" ? 9 :
80
+ fallbackResult.signal === "SIGINT" ? 2 : 1;
74
81
  process.exit(128 + signalNum);
75
82
  }
76
-
77
- process.exit(result.status ?? 1);
83
+
84
+ process.exit(fallbackResult.status ?? 1);
78
85
  }
79
86
 
80
87
  main();
package/dist/cli/index.js CHANGED
@@ -6117,6 +6117,59 @@ var init_model_resolver = __esm(() => {
6117
6117
  init_model_availability();
6118
6118
  });
6119
6119
 
6120
+ // src/shared/perf-timer.ts
6121
+ class PerfTimer {
6122
+ marks = new Map;
6123
+ spans = [];
6124
+ mark(name) {
6125
+ this.marks.set(name, performance.now());
6126
+ }
6127
+ measure(name, startMark, endMark) {
6128
+ const start = this.marks.get(startMark);
6129
+ if (start === undefined)
6130
+ return 0;
6131
+ const end = endMark ? this.marks.get(endMark) ?? performance.now() : performance.now();
6132
+ const duration = end - start;
6133
+ this.spans.push({ name, durationMs: duration, start, end });
6134
+ return duration;
6135
+ }
6136
+ getReport() {
6137
+ return [...this.spans];
6138
+ }
6139
+ reset() {
6140
+ this.marks.clear();
6141
+ this.spans = [];
6142
+ }
6143
+ static formatDuration(start, end, options) {
6144
+ const ms = (end ?? new Date).getTime() - start.getTime();
6145
+ const absMs = Math.abs(ms);
6146
+ const totalSeconds = absMs / 1000;
6147
+ const seconds = Math.floor(totalSeconds);
6148
+ const minutes = Math.floor(seconds / 60);
6149
+ const hours = Math.floor(minutes / 60);
6150
+ const precision = options?.precision ?? "full";
6151
+ if (hours > 0) {
6152
+ if (precision === "compact") {
6153
+ return `${hours}h ${minutes % 60}m`;
6154
+ }
6155
+ return `${hours}h ${minutes % 60}m ${seconds % 60}s`;
6156
+ }
6157
+ if (minutes > 0) {
6158
+ return `${minutes}m ${seconds % 60}s`;
6159
+ }
6160
+ if (seconds === 0 && absMs > 0) {
6161
+ return `${(absMs / 1000).toFixed(1)}s`;
6162
+ }
6163
+ return `${seconds}s`;
6164
+ }
6165
+ }
6166
+
6167
+ // src/shared/perf-tracer.ts
6168
+ var init_perf_tracer = () => {};
6169
+
6170
+ // src/shared/fileio-monitor.ts
6171
+ var init_fileio_monitor = () => {};
6172
+
6120
6173
  // src/shared/index.ts
6121
6174
  var init_shared = __esm(() => {
6122
6175
  init_frontmatter();
@@ -6144,6 +6197,8 @@ var init_shared = __esm(() => {
6144
6197
  init_model_requirements();
6145
6198
  init_model_resolver();
6146
6199
  init_model_availability();
6200
+ init_perf_tracer();
6201
+ init_fileio_monitor();
6147
6202
  });
6148
6203
 
6149
6204
  // src/cli/model-fallback.ts
@@ -8375,7 +8430,7 @@ var import_picocolors2 = __toESM(require_picocolors(), 1);
8375
8430
  // package.json
8376
8431
  var package_default = {
8377
8432
  name: "@skj1724/oh-my-opencode",
8378
- version: "3.19.3",
8433
+ version: "3.19.5",
8379
8434
  description: "The Best AI Agent Harness - Batteries-Included OpenCode Plugin with Multi-Model Orchestration, Parallel Background Agents, and Crafted LSP/AST Tools",
8380
8435
  main: "dist/index.js",
8381
8436
  types: "dist/index.d.ts",
@@ -24913,6 +24968,7 @@ var HookNameSchema = exports_external.enum([
24913
24968
  "edit-error-recovery",
24914
24969
  "delegate-task-retry",
24915
24970
  "prometheus-md-only",
24971
+ "perf-profiler",
24916
24972
  "start-work",
24917
24973
  "atlas"
24918
24974
  ]);
@@ -25026,11 +25082,21 @@ var DynamicContextPruningConfigSchema = exports_external.object({
25026
25082
  }).optional()
25027
25083
  }).optional()
25028
25084
  });
25085
+ var ProfilingConfigSchema = exports_external.object({
25086
+ enabled: exports_external.boolean().default(false),
25087
+ output_dir: exports_external.string().optional(),
25088
+ slow_threshold_ms: exports_external.number().default(100),
25089
+ memory_snapshot_interval: exports_external.number().default(5),
25090
+ trace_api: exports_external.boolean().default(true),
25091
+ trace_fileio: exports_external.boolean().default(true),
25092
+ trace_polling: exports_external.boolean().default(true)
25093
+ });
25029
25094
  var ExperimentalConfigSchema = exports_external.object({
25030
25095
  aggressive_truncation: exports_external.boolean().optional(),
25031
25096
  auto_resume: exports_external.boolean().optional(),
25032
25097
  truncate_all_tool_outputs: exports_external.boolean().optional(),
25033
- dynamic_context_pruning: DynamicContextPruningConfigSchema.optional()
25098
+ dynamic_context_pruning: DynamicContextPruningConfigSchema.optional(),
25099
+ profiling: ProfilingConfigSchema.optional()
25034
25100
  });
25035
25101
  var SkillSourceSchema = exports_external.union([
25036
25102
  exports_external.string(),
@@ -71,6 +71,7 @@ export declare const HookNameSchema: z.ZodEnum<{
71
71
  "edit-error-recovery": "edit-error-recovery";
72
72
  "delegate-task-retry": "delegate-task-retry";
73
73
  "prometheus-md-only": "prometheus-md-only";
74
+ "perf-profiler": "perf-profiler";
74
75
  "start-work": "start-work";
75
76
  }>;
76
77
  export declare const BuiltinCommandNameSchema: z.ZodEnum<{
@@ -890,6 +891,15 @@ export declare const DynamicContextPruningConfigSchema: z.ZodObject<{
890
891
  }, z.core.$strip>>;
891
892
  }, z.core.$strip>>;
892
893
  }, z.core.$strip>;
894
+ export declare const ProfilingConfigSchema: z.ZodObject<{
895
+ enabled: z.ZodDefault<z.ZodBoolean>;
896
+ output_dir: z.ZodOptional<z.ZodString>;
897
+ slow_threshold_ms: z.ZodDefault<z.ZodNumber>;
898
+ memory_snapshot_interval: z.ZodDefault<z.ZodNumber>;
899
+ trace_api: z.ZodDefault<z.ZodBoolean>;
900
+ trace_fileio: z.ZodDefault<z.ZodBoolean>;
901
+ trace_polling: z.ZodDefault<z.ZodBoolean>;
902
+ }, z.core.$strip>;
893
903
  export declare const ExperimentalConfigSchema: z.ZodObject<{
894
904
  aggressive_truncation: z.ZodOptional<z.ZodBoolean>;
895
905
  auto_resume: z.ZodOptional<z.ZodBoolean>;
@@ -920,6 +930,15 @@ export declare const ExperimentalConfigSchema: z.ZodObject<{
920
930
  }, z.core.$strip>>;
921
931
  }, z.core.$strip>>;
922
932
  }, z.core.$strip>>;
933
+ profiling: z.ZodOptional<z.ZodObject<{
934
+ enabled: z.ZodDefault<z.ZodBoolean>;
935
+ output_dir: z.ZodOptional<z.ZodString>;
936
+ slow_threshold_ms: z.ZodDefault<z.ZodNumber>;
937
+ memory_snapshot_interval: z.ZodDefault<z.ZodNumber>;
938
+ trace_api: z.ZodDefault<z.ZodBoolean>;
939
+ trace_fileio: z.ZodDefault<z.ZodBoolean>;
940
+ trace_polling: z.ZodDefault<z.ZodBoolean>;
941
+ }, z.core.$strip>>;
923
942
  }, z.core.$strip>;
924
943
  export declare const SkillSourceSchema: z.ZodUnion<readonly [z.ZodString, z.ZodObject<{
925
944
  path: z.ZodString;
@@ -1043,6 +1062,7 @@ export declare const OhMyOpenCodeConfigSchema: z.ZodObject<{
1043
1062
  "edit-error-recovery": "edit-error-recovery";
1044
1063
  "delegate-task-retry": "delegate-task-retry";
1045
1064
  "prometheus-md-only": "prometheus-md-only";
1065
+ "perf-profiler": "perf-profiler";
1046
1066
  "start-work": "start-work";
1047
1067
  }>>>;
1048
1068
  disabled_commands: z.ZodOptional<z.ZodArray<z.ZodEnum<{
@@ -1778,6 +1798,15 @@ export declare const OhMyOpenCodeConfigSchema: z.ZodObject<{
1778
1798
  }, z.core.$strip>>;
1779
1799
  }, z.core.$strip>>;
1780
1800
  }, z.core.$strip>>;
1801
+ profiling: z.ZodOptional<z.ZodObject<{
1802
+ enabled: z.ZodDefault<z.ZodBoolean>;
1803
+ output_dir: z.ZodOptional<z.ZodString>;
1804
+ slow_threshold_ms: z.ZodDefault<z.ZodNumber>;
1805
+ memory_snapshot_interval: z.ZodDefault<z.ZodNumber>;
1806
+ trace_api: z.ZodDefault<z.ZodBoolean>;
1807
+ trace_fileio: z.ZodDefault<z.ZodBoolean>;
1808
+ trace_polling: z.ZodDefault<z.ZodBoolean>;
1809
+ }, z.core.$strip>>;
1781
1810
  }, z.core.$strip>>;
1782
1811
  auto_update: z.ZodOptional<z.ZodBoolean>;
1783
1812
  skills: z.ZodOptional<z.ZodUnion<readonly [z.ZodArray<z.ZodString>, z.ZodIntersection<z.ZodRecord<z.ZodString, z.ZodUnion<readonly [z.ZodBoolean, z.ZodObject<{
@@ -1833,6 +1862,7 @@ export type SisyphusAgentConfig = z.infer<typeof SisyphusAgentConfigSchema>;
1833
1862
  export type CommentCheckerConfig = z.infer<typeof CommentCheckerConfigSchema>;
1834
1863
  export type ExperimentalConfig = z.infer<typeof ExperimentalConfigSchema>;
1835
1864
  export type DynamicContextPruningConfig = z.infer<typeof DynamicContextPruningConfigSchema>;
1865
+ export type ProfilingConfig = z.infer<typeof ProfilingConfigSchema>;
1836
1866
  export type SkillsConfig = z.infer<typeof SkillsConfigSchema>;
1837
1867
  export type SkillDefinition = z.infer<typeof SkillDefinitionSchema>;
1838
1868
  export type RalphLoopConfig = z.infer<typeof RalphLoopConfigSchema>;
@@ -1,3 +1,4 @@
1
1
  export * from "./types";
2
2
  export { BackgroundManager } from "./manager";
3
3
  export { ConcurrencyManager } from "./concurrency";
4
+ export { PerformanceAggregator } from "./perf-aggregator";
@@ -1,5 +1,6 @@
1
1
  import type { PluginInput } from "@opencode-ai/plugin";
2
2
  import type { BackgroundTask, LaunchInput, ResumeInput } from "./types";
3
+ import type { PerfTracer } from "../../shared/perf-tracer";
3
4
  import type { BackgroundTaskConfig } from "../../config/schema";
4
5
  interface EventProperties {
5
6
  sessionID?: string;
@@ -25,9 +26,12 @@ export declare class BackgroundManager {
25
26
  private concurrencyManager;
26
27
  private shutdownTriggered;
27
28
  private config?;
29
+ private perfAggregator;
30
+ private perfTracer?;
28
31
  private queuesByKey;
29
32
  private processingKeys;
30
33
  constructor(ctx: PluginInput, config?: BackgroundTaskConfig);
34
+ setPerfTracer(tracer: PerfTracer): void;
31
35
  launch(input: LaunchInput): Promise<BackgroundTask>;
32
36
  private processKey;
33
37
  private startTask;
@@ -89,7 +93,6 @@ export declare class BackgroundManager {
89
93
  */
90
94
  private tryCompleteTask;
91
95
  private notifyParentSession;
92
- private formatDuration;
93
96
  private hasRunningTasks;
94
97
  private pruneStaleTasksAndNotifications;
95
98
  private checkAndInterruptStaleTasks;
@@ -0,0 +1,26 @@
1
+ import type { BackgroundTask } from "./types";
2
+ export interface AgentStat {
3
+ count: number;
4
+ avgMs: number;
5
+ p50Ms: number;
6
+ p95Ms: number;
7
+ totalMs: number;
8
+ }
9
+ export interface AggregatedReport {
10
+ totalTasks: number;
11
+ tasksByAgent: Map<string, AgentStat>;
12
+ totalToolCalls: number;
13
+ queueWaitStats: {
14
+ avgMs: number;
15
+ p50Ms: number;
16
+ p95Ms: number;
17
+ maxMs: number;
18
+ };
19
+ }
20
+ export declare class PerformanceAggregator {
21
+ private tasks;
22
+ recordTaskCompletion(task: BackgroundTask): void;
23
+ getReport(): AggregatedReport;
24
+ reset(): void;
25
+ get taskCount(): number;
26
+ }
@@ -1,10 +1,25 @@
1
1
  export type BackgroundTaskStatus = "pending" | "running" | "completed" | "error" | "cancelled";
2
+ export interface PhaseTiming {
3
+ /** queuedAt -> startedAt in ms */
4
+ queueWaitMs: number;
5
+ /** startedAt -> completedAt in ms */
6
+ totalRunMs: number;
7
+ /** startedAt -> first assistant message in ms */
8
+ firstResponseMs?: number;
9
+ /** startedAt -> last message in ms */
10
+ lastResponseMs?: number;
11
+ /** Total tool call count */
12
+ toolCallCount: number;
13
+ /** toolCallCount / totalRunMs * 60000 (calls per minute) */
14
+ toolCallRate?: number;
15
+ }
2
16
  export interface TaskProgress {
3
17
  toolCalls: number;
4
18
  lastTool?: string;
5
19
  lastUpdate: Date;
6
20
  lastMessage?: string;
7
21
  lastMessageAt?: Date;
22
+ phaseTiming?: PhaseTiming;
8
23
  }
9
24
  export interface BackgroundTask {
10
25
  id: string;
@@ -8,11 +8,5 @@ export interface StoredMessage {
8
8
  tools?: Record<string, ToolPermission>;
9
9
  }
10
10
  export declare function findNearestMessageWithFields(messageDir: string): StoredMessage | null;
11
- /**
12
- * Finds the FIRST (oldest) message in the session with agent field.
13
- * This is used to get the original agent that started the session,
14
- * avoiding issues where newer messages may have a different agent
15
- * due to OpenCode's internal agent switching.
16
- */
17
11
  export declare function findFirstMessageWithAgent(messageDir: string): string | null;
18
12
  export declare function injectHookMessage(sessionID: string, hookContent: string, originalMessage: OriginalMessageContext): boolean;
@@ -28,3 +28,4 @@ export { createStartWorkHook } from "./start-work";
28
28
  export { createAtlasHook } from "./atlas";
29
29
  export { createDelegateTaskRetryHook } from "./delegate-task-retry";
30
30
  export { createQuestionLabelTruncatorHook } from "./question-label-truncator";
31
+ export { createPerfProfilerHook } from "./perf-profiler";
@@ -0,0 +1,2 @@
1
+ import type { PerfProfilerOptions, PerfProfilerHook } from "./types";
2
+ export declare function createPerfProfilerHook(options: PerfProfilerOptions): PerfProfilerHook;
@@ -0,0 +1 @@
1
+ export {};
@@ -0,0 +1,42 @@
1
+ import type { ProfilingConfig } from "../../config/schema";
2
+ import type { PerfTracer } from "../../shared/perf-tracer";
3
+ export interface PerfProfilerOptions {
4
+ config: ProfilingConfig;
5
+ tracer: PerfTracer;
6
+ /** 注册 Map/Set size 采集回调,key 为探针名称,fn 返回当前 size */
7
+ memoryProbes?: Record<string, () => number>;
8
+ }
9
+ export interface PerfProfilerHook {
10
+ "event": (input: {
11
+ event: {
12
+ type: string;
13
+ properties?: unknown;
14
+ };
15
+ }) => Promise<void>;
16
+ "tool.execute.before": (input: {
17
+ tool: string;
18
+ sessionID: string;
19
+ callID: string;
20
+ }, output: {
21
+ args: Record<string, unknown>;
22
+ }) => Promise<void>;
23
+ "tool.execute.after": (input: {
24
+ tool: string;
25
+ sessionID: string;
26
+ callID: string;
27
+ }, output: {
28
+ title: string;
29
+ output: string;
30
+ metadata: unknown;
31
+ }) => Promise<void>;
32
+ "chat.message": (input: {
33
+ sessionID: string;
34
+ agent?: string;
35
+ }, output: {
36
+ message: Record<string, unknown>;
37
+ parts: Array<{
38
+ type: string;
39
+ text?: string;
40
+ }>;
41
+ }) => Promise<void>;
42
+ }
@@ -1,10 +1,34 @@
1
- import type { ThinkModeInput } from "./types";
2
1
  export * from "./detector";
3
2
  export * from "./switcher";
4
3
  export * from "./types";
5
4
  export declare function clearThinkModeState(sessionID: string): void;
6
5
  export declare function createThinkModeHook(): {
7
- "chat.params": (output: ThinkModeInput, sessionID: string) => Promise<void>;
6
+ "chat.params": (input: {
7
+ sessionID: string;
8
+ agent: string;
9
+ model: {
10
+ id: string;
11
+ providerID: string;
12
+ };
13
+ provider: {
14
+ source: string;
15
+ info: unknown;
16
+ options: Record<string, unknown>;
17
+ };
18
+ message: {
19
+ role: string;
20
+ parts?: Array<{
21
+ type: string;
22
+ text?: string;
23
+ }>;
24
+ };
25
+ }, output: {
26
+ temperature: number;
27
+ topP: number;
28
+ topK: number;
29
+ maxOutputTokens: number | undefined;
30
+ options: Record<string, unknown>;
31
+ }) => Promise<void>;
8
32
  event: ({ event }: {
9
33
  event: {
10
34
  type: string;
@@ -1,3 +1,5 @@
1
+ import type { Model, UserMessage } from "@opencode-ai/sdk";
2
+ import type { ProviderContext } from "@opencode-ai/plugin";
1
3
  export interface ThinkModeState {
2
4
  requested: boolean;
3
5
  modelSwitched: boolean;
@@ -5,17 +7,17 @@ export interface ThinkModeState {
5
7
  providerID?: string;
6
8
  modelID?: string;
7
9
  }
8
- export interface ModelRef {
9
- providerID: string;
10
- modelID: string;
11
- }
12
- export interface MessageWithModel {
13
- model?: ModelRef;
14
- }
15
10
  export interface ThinkModeInput {
16
- parts: Array<{
17
- type: string;
18
- text?: string;
19
- }>;
20
- message: MessageWithModel;
11
+ sessionID: string;
12
+ agent: string;
13
+ model: Model;
14
+ provider: ProviderContext;
15
+ message: UserMessage;
16
+ }
17
+ export interface ThinkModeOutput {
18
+ temperature: number;
19
+ topP: number;
20
+ topK: number;
21
+ maxOutputTokens: number | undefined;
22
+ options: Record<string, unknown>;
21
23
  }