@actiondock/core 2.0.9 → 2.0.11-beta.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/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@actiondock/core",
3
- "version": "2.0.9",
3
+ "version": "2.0.11-beta.0",
4
4
  "description": "ActionDock Core Engine - Project loader, runtime execution, SQLite storage, standalone builder, and skill exporter",
5
5
  "type": "module",
6
6
  "main": "./src/index.ts",
@@ -27,7 +27,7 @@
27
27
  "test": "bun test"
28
28
  },
29
29
  "dependencies": {
30
- "@actiondock/sdk": "^2.0.9",
30
+ "@actiondock/sdk": "2.0.11-beta.0",
31
31
  "ajv": "^8.17.1",
32
32
  "ajv-formats": "^3.0.1",
33
33
  "yaml": "^2.7.0"
@@ -1,2 +1 @@
1
- export * from "./builder";
2
1
  export * from "./templates";
@@ -27,7 +27,7 @@ export function generateStandaloneEntrypoint(
27
27
  configDefs?: Record<string, unknown>
28
28
  ): string {
29
29
  // Resolve path to standalone runtime inside @actiondock/cli
30
- const standaloneRuntimePath = resolve(__dirname, "../runtime/standalone");
30
+ const standaloneRuntimePath = resolve(import.meta.dirname, "../runtime/standalone");
31
31
 
32
32
  const imports = actions
33
33
  .map((a, idx) => `import action_${idx} from ${JSON.stringify(a.filePath)};`)
@@ -72,12 +72,4 @@ export interface IndexedAction {
72
72
  projectRoot: string;
73
73
  }
74
74
 
75
- /**
76
- * 模块加载器接口。
77
- */
78
- export interface ModuleLoader {
79
- load<T>(file: string, options: {
80
- projectRoot: string;
81
- tsconfigPath?: string;
82
- }): Promise<T>;
83
- }
75
+ export type { ModuleLoader } from "../runtime/module-loader";
@@ -1,9 +1,10 @@
1
- import { existsSync } from "node:fs";
2
- import { delimiter, join } from "node:path";
3
- import { findProjectRoot, loadActions, loadPlaybooks, loadProjectConfig } from "../project/loader";
1
+ import { existsSync, statSync } from "node:fs";
2
+ import { delimiter, join, relative } from "node:path";
3
+ import { discoverActionFiles, findProjectRoot, loadActions, loadPlaybooks, loadProjectConfig } from "../project/loader";
4
+ import { loadManifest, MANIFEST_FILE_NAME } from "../project/manifest";
4
5
  import { getRegistryStatus } from "../registry/registry";
5
6
  import { createGlobalStorage, createStorage } from "../storage";
6
- import { getActionDockHome } from "../utils";
7
+ import { findExecutable, getActionDockHome } from "../utils";
7
8
  import type { DoctorCheckItem, DoctorReport } from "./types";
8
9
 
9
10
  function compareSemver(v1: string, v2: string): number {
@@ -18,36 +19,6 @@ function compareSemver(v1: string, v2: string): number {
18
19
  return 0;
19
20
  }
20
21
 
21
- function findExecutable(command: string): string | null {
22
- if (typeof (globalThis as any).Bun !== "undefined" && typeof (globalThis as any).Bun.which === "function") {
23
- try {
24
- const bPath = (globalThis as any).Bun.which(command);
25
- if (bPath) return bPath;
26
- } catch {}
27
- }
28
- const hasPathSep = command.includes("/") || command.includes("\\");
29
- if (hasPathSep) {
30
- return existsSync(command) ? command : null;
31
- }
32
- const pathEnv = process.env.PATH || "";
33
- const dirs = pathEnv.split(delimiter);
34
- const isWindows = process.platform === "win32";
35
- const pathext = isWindows
36
- ? (process.env.PATHEXT || ".COM;.EXE;.BAT;.CMD").split(";")
37
- : [""];
38
-
39
- for (const dir of dirs) {
40
- if (!dir) continue;
41
- for (const ext of pathext) {
42
- const candidate = join(dir, isWindows && !command.includes(".") ? command + ext : command);
43
- if (existsSync(candidate)) {
44
- return candidate;
45
- }
46
- }
47
- }
48
- return null;
49
- }
50
-
51
22
  export async function runDoctorChecks(options?: {
52
23
  cwd?: string;
53
24
  packageIdOrPath?: string;
@@ -244,15 +215,23 @@ export async function runDoctorChecks(options?: {
244
215
 
245
216
  // Actions Check
246
217
  try {
247
- const actions = await loadActions(projectRoot, config.actionsDir);
248
- if (actions.size === 0) {
218
+ const manifest = loadManifest(projectRoot);
219
+ let actionsCount = 0;
220
+ if (manifest?.actions) {
221
+ actionsCount = Object.keys(manifest.actions).length;
222
+ } else {
223
+ const actions = await loadActions(projectRoot, config.actionsDir, { autoInstall: false });
224
+ actionsCount = actions.size;
225
+ }
226
+
227
+ if (actionsCount === 0) {
249
228
  checks.push({
250
229
  id: "project.actions",
251
230
  category: "project",
252
231
  name: "Actions",
253
232
  status: "warn",
254
233
  message: `No actions found in '${config.actionsDir || "actions"}'`,
255
- fix: "Run 'ad action create <id>' to create your first action",
234
+ fix: "Run 'ad action new <id>' to create your first action",
256
235
  });
257
236
  } else {
258
237
  checks.push({
@@ -260,7 +239,7 @@ export async function runDoctorChecks(options?: {
260
239
  category: "project",
261
240
  name: "Actions",
262
241
  status: "ok",
263
- message: `${actions.size} action(s) valid and loaded`,
242
+ message: `${actionsCount} action(s) valid and loaded`,
264
243
  });
265
244
  }
266
245
  } catch (err: any) {
@@ -273,6 +252,88 @@ export async function runDoctorChecks(options?: {
273
252
  });
274
253
  }
275
254
 
255
+ // Project Manifest Check (Lightweight Static Detection)
256
+ try {
257
+ const manifestPath = join(projectRoot, MANIFEST_FILE_NAME);
258
+ const manifest = loadManifest(projectRoot);
259
+ const actionFiles = discoverActionFiles(projectRoot, config.actionsDir || "actions");
260
+
261
+ if (!existsSync(manifestPath)) {
262
+ if (actionFiles.length > 0) {
263
+ checks.push({
264
+ id: "project.manifest",
265
+ category: "project",
266
+ name: "Action Manifest",
267
+ status: "warn",
268
+ message: `${MANIFEST_FILE_NAME} not found (${actionFiles.length} action source file(s) exist)`,
269
+ fix: "Run 'ad action sync' to generate manifest",
270
+ });
271
+ }
272
+ } else if (manifest && manifest.actions) {
273
+ const missingFiles = Object.entries(manifest.actions)
274
+ .filter(([_, item]) => !existsSync(join(projectRoot, item.entry)))
275
+ .map(([id, item]) => `${id} (${item.entry})`);
276
+
277
+ const manifestEntries = new Set(
278
+ Object.values(manifest.actions).map((a) => a.entry.replace(/\\/g, "/"))
279
+ );
280
+ const untracked = actionFiles
281
+ .map((f) => relative(projectRoot, f).replace(/\\/g, "/"))
282
+ .filter((rel) => !manifestEntries.has(rel));
283
+
284
+ if (missingFiles.length > 0) {
285
+ checks.push({
286
+ id: "project.manifest",
287
+ category: "project",
288
+ name: "Action Manifest",
289
+ status: "warn",
290
+ message: `${missingFiles.length} action(s) in manifest point to missing files: ${missingFiles.join(", ")}`,
291
+ fix: "Run 'ad action sync' to synchronize manifest",
292
+ });
293
+ } else if (untracked.length > 0) {
294
+ checks.push({
295
+ id: "project.manifest",
296
+ category: "project",
297
+ name: "Action Manifest",
298
+ status: "warn",
299
+ message: `${untracked.length} action file(s) not declared in manifest: ${untracked.join(", ")}`,
300
+ fix: "Run 'ad action sync' to synchronize manifest",
301
+ });
302
+ } else {
303
+ const manifestStat = statSync(manifestPath);
304
+ const newerFiles = actionFiles.filter(
305
+ (f) => statSync(f).mtimeMs > manifestStat.mtimeMs + 2000
306
+ );
307
+ if (newerFiles.length > 0) {
308
+ checks.push({
309
+ id: "project.manifest",
310
+ category: "project",
311
+ name: "Action Manifest",
312
+ status: "ok",
313
+ message: `Manifest valid (Note: ${newerFiles.length} action file(s) modified after manifest; run 'ad action sync' if definitions changed)`,
314
+ });
315
+ } else {
316
+ checks.push({
317
+ id: "project.manifest",
318
+ category: "project",
319
+ name: "Action Manifest",
320
+ status: "ok",
321
+ message: "Manifest synchronized with action files",
322
+ });
323
+ }
324
+ }
325
+ }
326
+ } catch (err: any) {
327
+ checks.push({
328
+ id: "project.manifest",
329
+ category: "project",
330
+ name: "Action Manifest",
331
+ status: "error",
332
+ message: `Failed to inspect manifest: ${err.message}`,
333
+ });
334
+ }
335
+
336
+
276
337
  // Playbooks Check
277
338
  try {
278
339
  const playbooks = loadPlaybooks(projectRoot, config.playbooksDir);
@@ -5,6 +5,7 @@ import type {
5
5
  ExecutionEvent,
6
6
  ExecutionResult,
7
7
  JsonValue,
8
+ Logger,
8
9
  ProgressReporter,
9
10
  RunRecord,
10
11
  RunStatus,
@@ -159,6 +160,7 @@ export class DefaultExecutionService implements ExecutionService {
159
160
  }
160
161
  }
161
162
 
163
+ const runId = randomUUID();
162
164
  let sequence = 0;
163
165
  type EventPayload =
164
166
  | { type: "log"; level: "debug" | "info" | "warn" | "error"; message: string; data?: JsonValue }
@@ -166,16 +168,11 @@ export class DefaultExecutionService implements ExecutionService {
166
168
  | { type: "status"; status: RunStatus }
167
169
  | { type: "finish"; result: ExecutionResult };
168
170
 
169
- const handle = this.runner.start(action, input, {
170
- signal: controller.signal,
171
- timeoutMs: options.timeoutMs,
172
- });
173
-
174
171
  const emitEvent = (payload: EventPayload) => {
175
172
  const evt: ExecutionEvent = {
176
173
  ...payload,
177
- runId: handle.runId,
178
- rootRunId: handle.runId,
174
+ runId,
175
+ rootRunId: runId,
179
176
  sequence: sequence++,
180
177
  timestamp: new Date().toISOString(),
181
178
  };
@@ -193,6 +190,49 @@ export class DefaultExecutionService implements ExecutionService {
193
190
  },
194
191
  };
195
192
 
193
+ const executionLogger: Logger = {
194
+ debug(message: string, data?: unknown) {
195
+ emitEvent({
196
+ type: "log",
197
+ level: "debug",
198
+ message,
199
+ data: data as JsonValue | undefined,
200
+ });
201
+ },
202
+ info(message: string, data?: unknown) {
203
+ emitEvent({
204
+ type: "log",
205
+ level: "info",
206
+ message,
207
+ data: data as JsonValue | undefined,
208
+ });
209
+ },
210
+ warn(message: string, data?: unknown) {
211
+ emitEvent({
212
+ type: "log",
213
+ level: "warn",
214
+ message,
215
+ data: data as JsonValue | undefined,
216
+ });
217
+ },
218
+ error(message: string, data?: unknown) {
219
+ emitEvent({
220
+ type: "log",
221
+ level: "error",
222
+ message,
223
+ data: data as JsonValue | undefined,
224
+ });
225
+ },
226
+ };
227
+
228
+ const handle = this.runner.start(action, input, {
229
+ runId,
230
+ signal: controller.signal,
231
+ timeoutMs: options.timeoutMs,
232
+ progress: progressReporter,
233
+ logger: executionLogger,
234
+ });
235
+
196
236
  const activeItem: ActiveRun = {
197
237
  runId: handle.runId,
198
238
  handle,
@@ -1,2 +1 @@
1
- export * from "./skill";
2
1
  export * from "./templates";