auriga-cli 1.15.2 → 1.17.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 +14 -2
- package/README.zh-CN.md +14 -2
- package/dist/api-types.d.ts +115 -0
- package/dist/api-types.js +4 -0
- package/dist/apply-handlers.d.ts +17 -0
- package/dist/apply-handlers.js +186 -0
- package/dist/catalog.json +3 -3
- package/dist/cli.d.ts +13 -0
- package/dist/cli.js +220 -0
- package/dist/help.js +2 -0
- package/dist/hooks.d.ts +30 -0
- package/dist/hooks.js +89 -0
- package/dist/plugins.d.ts +29 -0
- package/dist/plugins.js +137 -6
- package/dist/scan-catalog.d.ts +2 -0
- package/dist/scan-catalog.js +138 -0
- package/dist/server.d.ts +71 -0
- package/dist/server.js +759 -0
- package/dist/skills.d.ts +29 -0
- package/dist/skills.js +146 -3
- package/dist/state.d.ts +63 -0
- package/dist/state.js +623 -0
- package/dist/ui-fetch.d.ts +29 -0
- package/dist/ui-fetch.js +267 -0
- package/dist/utils.d.ts +22 -0
- package/dist/utils.js +58 -1
- package/dist/workflow.d.ts +22 -0
- package/dist/workflow.js +63 -0
- package/package.json +5 -3
|
@@ -0,0 +1,138 @@
|
|
|
1
|
+
// Build the scan-time Catalog (the shape src/state.ts consumes) from
|
|
2
|
+
// auriga-cli's installed package state. This bridges the build-time
|
|
3
|
+
// `dist/catalog.json` (which carries names + descriptions for the menu)
|
|
4
|
+
// and the runtime scanner's need for expected hashes + versions.
|
|
5
|
+
//
|
|
6
|
+
// Inputs (all under packageRoot):
|
|
7
|
+
// dist/catalog.json — names + descriptions for 5 categories
|
|
8
|
+
// skills-lock.json — expected SHA256 for every vendored skill
|
|
9
|
+
// .claude/plugins.json — Claude plugin entries (agent = "claude")
|
|
10
|
+
// .agents/plugins/install.json — Codex plugin entries (agent = "codex")
|
|
11
|
+
// .claude/hooks/<name>/index.mjs — runtime SHA256 = expected hash
|
|
12
|
+
// CLAUDE.md — `# auriga Workflow (vX.Y.Z)` provides
|
|
13
|
+
// workflowVersion
|
|
14
|
+
//
|
|
15
|
+
// Anything missing is treated as "no expectation" (empty hash / version)
|
|
16
|
+
// rather than throwing; scanState will still produce a structurally valid
|
|
17
|
+
// StateReport — items just classify as not-installed or installed
|
|
18
|
+
// depending on whether the user-side data exists.
|
|
19
|
+
import { createHash } from "node:crypto";
|
|
20
|
+
import { readFile } from "node:fs/promises";
|
|
21
|
+
import path from "node:path";
|
|
22
|
+
import { loadCatalog } from "./catalog.js";
|
|
23
|
+
const WORKFLOW_VERSION_RE = /^#\s*auriga Workflow\s*\(v([\d.]+)\)/m;
|
|
24
|
+
async function tryReadFile(p) {
|
|
25
|
+
try {
|
|
26
|
+
return await readFile(p, "utf8");
|
|
27
|
+
}
|
|
28
|
+
catch {
|
|
29
|
+
return null;
|
|
30
|
+
}
|
|
31
|
+
}
|
|
32
|
+
async function sha256File(p) {
|
|
33
|
+
const bytes = await readFile(p);
|
|
34
|
+
return createHash("sha256").update(bytes).digest("hex");
|
|
35
|
+
}
|
|
36
|
+
export async function buildScanCatalog(packageRoot) {
|
|
37
|
+
const dist = loadCatalog(packageRoot);
|
|
38
|
+
// Workflow version: parse from auriga-cli's own CLAUDE.md template.
|
|
39
|
+
// If missing, leave as empty string so workflow always classifies as
|
|
40
|
+
// not-installed (no expectation set).
|
|
41
|
+
const claudeMd = await tryReadFile(path.join(packageRoot, "CLAUDE.md"));
|
|
42
|
+
const m = claudeMd ? WORKFLOW_VERSION_RE.exec(claudeMd) : null;
|
|
43
|
+
const workflowVersion = m ? m[1] : "";
|
|
44
|
+
// Skills + recommended: hashes from skills-lock.json.
|
|
45
|
+
let lock = {};
|
|
46
|
+
const lockText = await tryReadFile(path.join(packageRoot, "skills-lock.json"));
|
|
47
|
+
if (lockText) {
|
|
48
|
+
try {
|
|
49
|
+
lock = JSON.parse(lockText);
|
|
50
|
+
}
|
|
51
|
+
catch {
|
|
52
|
+
// corrupted lock → no expectations; user state still classifies safely
|
|
53
|
+
}
|
|
54
|
+
}
|
|
55
|
+
const skills = {};
|
|
56
|
+
for (const entry of dist.workflowSkills) {
|
|
57
|
+
skills[entry.name] = {
|
|
58
|
+
description: entry.description,
|
|
59
|
+
expectedHash: lock.skills?.[entry.name]?.computedHash ?? "",
|
|
60
|
+
isWorkflow: true,
|
|
61
|
+
};
|
|
62
|
+
}
|
|
63
|
+
const recommendedSkills = {};
|
|
64
|
+
for (const entry of dist.recommendedSkills) {
|
|
65
|
+
recommendedSkills[entry.name] = {
|
|
66
|
+
description: entry.description,
|
|
67
|
+
expectedHash: lock.skills?.[entry.name]?.computedHash ?? "",
|
|
68
|
+
};
|
|
69
|
+
}
|
|
70
|
+
// Plugins: split by agent based on which config file lists them. A
|
|
71
|
+
// plugin can appear in both registries (cross-agent plugins like
|
|
72
|
+
// auriga-go); we represent it once per agent.
|
|
73
|
+
const plugins = {};
|
|
74
|
+
const claudePluginsText = await tryReadFile(path.join(packageRoot, ".claude", "plugins.json"));
|
|
75
|
+
const claudeNames = new Set();
|
|
76
|
+
if (claudePluginsText) {
|
|
77
|
+
try {
|
|
78
|
+
const parsed = JSON.parse(claudePluginsText);
|
|
79
|
+
for (const p of parsed.plugins ?? []) {
|
|
80
|
+
if (p.name)
|
|
81
|
+
claudeNames.add(p.name);
|
|
82
|
+
}
|
|
83
|
+
}
|
|
84
|
+
catch {
|
|
85
|
+
/* ignore */
|
|
86
|
+
}
|
|
87
|
+
}
|
|
88
|
+
const codexInstallText = await tryReadFile(path.join(packageRoot, ".agents", "plugins", "install.json"));
|
|
89
|
+
const codexNames = new Set();
|
|
90
|
+
if (codexInstallText) {
|
|
91
|
+
try {
|
|
92
|
+
const parsed = JSON.parse(codexInstallText);
|
|
93
|
+
for (const p of parsed.plugins ?? []) {
|
|
94
|
+
if (p.name)
|
|
95
|
+
codexNames.add(p.name);
|
|
96
|
+
}
|
|
97
|
+
}
|
|
98
|
+
catch {
|
|
99
|
+
/* ignore */
|
|
100
|
+
}
|
|
101
|
+
}
|
|
102
|
+
for (const entry of dist.plugins) {
|
|
103
|
+
// Collect every agent that registers this plugin. A plugin can ship in
|
|
104
|
+
// both registries (cross-agent plugins like auriga-go); we emit it as
|
|
105
|
+
// a single multi-agent record so the UI shows one row + BOTH badge and
|
|
106
|
+
// Apply installs to each side.
|
|
107
|
+
const agents = [];
|
|
108
|
+
if (claudeNames.has(entry.name))
|
|
109
|
+
agents.push("claude");
|
|
110
|
+
if (codexNames.has(entry.name))
|
|
111
|
+
agents.push("codex");
|
|
112
|
+
if (agents.length === 0)
|
|
113
|
+
agents.push("claude"); // unknown defaults to claude
|
|
114
|
+
plugins[entry.name] = { description: entry.description, agents };
|
|
115
|
+
}
|
|
116
|
+
// Hooks: runtime SHA256 of each hook's index.mjs serves as the expected
|
|
117
|
+
// hash. If the file can't be read, leave the expectation empty so the
|
|
118
|
+
// hook classifies as not-installed.
|
|
119
|
+
const hooks = {};
|
|
120
|
+
for (const entry of dist.hooks) {
|
|
121
|
+
const hookEntry = path.join(packageRoot, ".claude", "hooks", entry.name, "index.mjs");
|
|
122
|
+
let expectedHash = "";
|
|
123
|
+
try {
|
|
124
|
+
expectedHash = await sha256File(hookEntry);
|
|
125
|
+
}
|
|
126
|
+
catch {
|
|
127
|
+
/* missing or unreadable hook payload; leave hash empty */
|
|
128
|
+
}
|
|
129
|
+
hooks[entry.name] = { description: entry.description, expectedHash };
|
|
130
|
+
}
|
|
131
|
+
return {
|
|
132
|
+
workflowVersion,
|
|
133
|
+
skills,
|
|
134
|
+
recommendedSkills,
|
|
135
|
+
plugins,
|
|
136
|
+
hooks,
|
|
137
|
+
};
|
|
138
|
+
}
|
package/dist/server.d.ts
ADDED
|
@@ -0,0 +1,71 @@
|
|
|
1
|
+
export type LogLevel = "info" | "warn" | "error";
|
|
2
|
+
export interface ApplyHandlerOptions {
|
|
3
|
+
onLog: (line: string, level: LogLevel) => void;
|
|
4
|
+
/** Installer scope from the ApplyItemRef. Forwarded as-is — handlers
|
|
5
|
+
* translate into the per-installer flag (`--scope project|user`). The
|
|
6
|
+
* workflow handler ignores it (workflow has no scope concept). */
|
|
7
|
+
scope?: "project" | "user";
|
|
8
|
+
/** Workflow CLAUDE.md language variant. Only meaningful for the workflow
|
|
9
|
+
* handler; other handlers ignore it. Omitted = "en". */
|
|
10
|
+
lang?: "en" | "zh-CN";
|
|
11
|
+
}
|
|
12
|
+
export type ApplyHandler = (action: ApplyAction, name: string, opts: ApplyHandlerOptions) => Promise<void>;
|
|
13
|
+
export interface ApplyHandlers {
|
|
14
|
+
workflow: ApplyHandler;
|
|
15
|
+
skill: ApplyHandler;
|
|
16
|
+
"recommended-skill": ApplyHandler;
|
|
17
|
+
plugin: ApplyHandler;
|
|
18
|
+
hook: ApplyHandler;
|
|
19
|
+
}
|
|
20
|
+
export interface ApplyCatalog {
|
|
21
|
+
workflow: Set<string>;
|
|
22
|
+
skill: Set<string>;
|
|
23
|
+
"recommended-skill": Set<string>;
|
|
24
|
+
plugin: Set<string>;
|
|
25
|
+
hook: Set<string>;
|
|
26
|
+
}
|
|
27
|
+
export interface StartServerOptions {
|
|
28
|
+
port?: number;
|
|
29
|
+
token: string;
|
|
30
|
+
cwd: string;
|
|
31
|
+
/** Where auriga-cli itself lives — source of dist/catalog.json,
|
|
32
|
+
* skills-lock.json, hook payloads, etc. Defaults to cwd, which is
|
|
33
|
+
* correct when running tests from the auriga-cli checkout. CLI mode
|
|
34
|
+
* must pass getPackageRoot() so the server uses the installed package
|
|
35
|
+
* rather than the user's project. */
|
|
36
|
+
packageRoot?: string;
|
|
37
|
+
/** Idle-shutdown timeout in ms. The browser POSTs /api/ping every 5s;
|
|
38
|
+
* if no ping arrives for this duration, the server shuts down
|
|
39
|
+
* gracefully (closing-browser-closes-server UX). `0` disables the
|
|
40
|
+
* heartbeat (used by tests so a single suite doesn't time-bomb). */
|
|
41
|
+
heartbeatTimeoutMs?: number;
|
|
42
|
+
/** Apply handlers per category. When omitted, /api/apply falls back to
|
|
43
|
+
* built-in installers wired by the CLI. Tests inject mocks to make apply
|
|
44
|
+
* behavior deterministic without touching real installers. */
|
|
45
|
+
applyHandlers?: ApplyHandlers;
|
|
46
|
+
/** Per-category name whitelist. When set, /api/apply rejects (400) any
|
|
47
|
+
* item whose name is not present in the matching category's Set. When
|
|
48
|
+
* omitted, name membership is not enforced (CLI builds a default
|
|
49
|
+
* catalog at boot time). */
|
|
50
|
+
applyCatalog?: ApplyCatalog;
|
|
51
|
+
/** Directory whose contents are served for non-/api paths (the extracted
|
|
52
|
+
* UI bundle). When undefined, every static path returns 404 — useful in
|
|
53
|
+
* tests and the M1 server smoke checks. */
|
|
54
|
+
uiDir?: string;
|
|
55
|
+
/** Max time to wait for an in-flight job during graceful shutdown before
|
|
56
|
+
* force-closing sockets (spec §4.3 / §6.6). Defaults to 30000 ms in
|
|
57
|
+
* production; tests override to a small value (e.g. 200 ms) so they
|
|
58
|
+
* don't time-bomb. */
|
|
59
|
+
shutdownGraceMs?: number;
|
|
60
|
+
}
|
|
61
|
+
export interface RunningServer {
|
|
62
|
+
port: number;
|
|
63
|
+
/** Explicit shutdown. Idempotent. */
|
|
64
|
+
close(): Promise<void>;
|
|
65
|
+
/** Resolves when the server has fully stopped — either via close() or the
|
|
66
|
+
* heartbeat-driven shutdown. CLI callers await this to block their event
|
|
67
|
+
* loop until "browser was closed" actually fires. */
|
|
68
|
+
closed: Promise<void>;
|
|
69
|
+
}
|
|
70
|
+
import type { ApplyAction } from "./api-types.js";
|
|
71
|
+
export declare function startServer(opts: StartServerOptions): Promise<RunningServer>;
|