vibeusage 0.2.23 → 0.3.1
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 +25 -20
- package/README.zh-CN.md +7 -2
- package/node_modules/@insforge/sdk/LICENSE +201 -201
- package/node_modules/@insforge/sdk/README.md +326 -259
- package/node_modules/@insforge/sdk/dist/index.d.mts +377 -182
- package/node_modules/@insforge/sdk/dist/index.d.ts +377 -182
- package/node_modules/@insforge/sdk/dist/index.js +1172 -677
- package/node_modules/@insforge/sdk/dist/index.js.map +1 -1
- package/node_modules/@insforge/sdk/dist/index.mjs +1171 -677
- package/node_modules/@insforge/sdk/dist/index.mjs.map +1 -1
- package/node_modules/@insforge/sdk/package.json +68 -68
- package/node_modules/@insforge/shared-schemas/dist/ai-api.schema.d.ts +1120 -43
- package/node_modules/@insforge/shared-schemas/dist/ai-api.schema.d.ts.map +1 -1
- package/node_modules/@insforge/shared-schemas/dist/ai-api.schema.js +179 -5
- package/node_modules/@insforge/shared-schemas/dist/ai-api.schema.js.map +1 -1
- package/node_modules/@insforge/shared-schemas/dist/ai.schema.d.ts +25 -25
- package/node_modules/@insforge/shared-schemas/dist/ai.schema.d.ts.map +1 -1
- package/node_modules/@insforge/shared-schemas/dist/ai.schema.js +2 -2
- package/node_modules/@insforge/shared-schemas/dist/ai.schema.js.map +1 -1
- package/node_modules/@insforge/shared-schemas/dist/auth-api.schema.d.ts +197 -51
- package/node_modules/@insforge/shared-schemas/dist/auth-api.schema.d.ts.map +1 -1
- package/node_modules/@insforge/shared-schemas/dist/auth-api.schema.js +87 -23
- package/node_modules/@insforge/shared-schemas/dist/auth-api.schema.js.map +1 -1
- package/node_modules/@insforge/shared-schemas/dist/auth.schema.d.ts +32 -3
- package/node_modules/@insforge/shared-schemas/dist/auth.schema.d.ts.map +1 -1
- package/node_modules/@insforge/shared-schemas/dist/auth.schema.js +21 -3
- package/node_modules/@insforge/shared-schemas/dist/auth.schema.js.map +1 -1
- package/node_modules/@insforge/shared-schemas/dist/cloud-events.schema.d.ts +380 -0
- package/node_modules/@insforge/shared-schemas/dist/cloud-events.schema.d.ts.map +1 -1
- package/node_modules/@insforge/shared-schemas/dist/cloud-events.schema.js +74 -0
- package/node_modules/@insforge/shared-schemas/dist/cloud-events.schema.js.map +1 -1
- package/node_modules/@insforge/shared-schemas/dist/database-api.schema.d.ts +13 -13
- package/node_modules/@insforge/shared-schemas/dist/database-api.schema.js +1 -1
- package/node_modules/@insforge/shared-schemas/dist/database-api.schema.js.map +1 -1
- package/node_modules/@insforge/shared-schemas/dist/deployments-api.schema.d.ts +735 -0
- package/node_modules/@insforge/shared-schemas/dist/deployments-api.schema.d.ts.map +1 -0
- package/node_modules/@insforge/shared-schemas/dist/deployments-api.schema.js +209 -0
- package/node_modules/@insforge/shared-schemas/dist/deployments-api.schema.js.map +1 -0
- package/node_modules/@insforge/shared-schemas/dist/deployments.schema.d.ts +37 -0
- package/node_modules/@insforge/shared-schemas/dist/deployments.schema.d.ts.map +1 -0
- package/node_modules/@insforge/shared-schemas/dist/deployments.schema.js +25 -0
- package/node_modules/@insforge/shared-schemas/dist/deployments.schema.js.map +1 -0
- package/node_modules/@insforge/shared-schemas/dist/docs.schema.d.ts +5 -1
- package/node_modules/@insforge/shared-schemas/dist/docs.schema.d.ts.map +1 -1
- package/node_modules/@insforge/shared-schemas/dist/docs.schema.js +34 -4
- package/node_modules/@insforge/shared-schemas/dist/docs.schema.js.map +1 -1
- package/node_modules/@insforge/shared-schemas/dist/email-api.schema.js +1 -1
- package/node_modules/@insforge/shared-schemas/dist/email-api.schema.js.map +1 -1
- package/node_modules/@insforge/shared-schemas/dist/functions-api.schema.d.ts +186 -6
- package/node_modules/@insforge/shared-schemas/dist/functions-api.schema.d.ts.map +1 -1
- package/node_modules/@insforge/shared-schemas/dist/functions-api.schema.js +21 -2
- package/node_modules/@insforge/shared-schemas/dist/functions-api.schema.js.map +1 -1
- package/node_modules/@insforge/shared-schemas/dist/functions.schema.d.ts +5 -5
- package/node_modules/@insforge/shared-schemas/dist/functions.schema.js +1 -1
- package/node_modules/@insforge/shared-schemas/dist/functions.schema.js.map +1 -1
- package/node_modules/@insforge/shared-schemas/dist/index.d.ts +24 -18
- package/node_modules/@insforge/shared-schemas/dist/index.d.ts.map +1 -1
- package/node_modules/@insforge/shared-schemas/dist/index.js +24 -18
- package/node_modules/@insforge/shared-schemas/dist/index.js.map +1 -1
- package/node_modules/@insforge/shared-schemas/dist/logs-api.schema.js +1 -1
- package/node_modules/@insforge/shared-schemas/dist/logs-api.schema.js.map +1 -1
- package/node_modules/@insforge/shared-schemas/dist/logs.schema.d.ts +43 -0
- package/node_modules/@insforge/shared-schemas/dist/logs.schema.d.ts.map +1 -1
- package/node_modules/@insforge/shared-schemas/dist/logs.schema.js +11 -0
- package/node_modules/@insforge/shared-schemas/dist/logs.schema.js.map +1 -1
- package/node_modules/@insforge/shared-schemas/dist/metadata.schema.d.ts +229 -172
- package/node_modules/@insforge/shared-schemas/dist/metadata.schema.d.ts.map +1 -1
- package/node_modules/@insforge/shared-schemas/dist/metadata.schema.js +27 -7
- package/node_modules/@insforge/shared-schemas/dist/metadata.schema.js.map +1 -1
- package/node_modules/@insforge/shared-schemas/dist/rate-limit-api.schema.d.ts +51 -0
- package/node_modules/@insforge/shared-schemas/dist/rate-limit-api.schema.d.ts.map +1 -0
- package/node_modules/@insforge/shared-schemas/dist/rate-limit-api.schema.js +31 -0
- package/node_modules/@insforge/shared-schemas/dist/rate-limit-api.schema.js.map +1 -0
- package/node_modules/@insforge/shared-schemas/dist/rate-limit.schema.d.ts +31 -0
- package/node_modules/@insforge/shared-schemas/dist/rate-limit.schema.d.ts.map +1 -0
- package/node_modules/@insforge/shared-schemas/dist/rate-limit.schema.js +12 -0
- package/node_modules/@insforge/shared-schemas/dist/rate-limit.schema.js.map +1 -0
- package/node_modules/@insforge/shared-schemas/dist/realtime-api.schema.d.ts +39 -20
- package/node_modules/@insforge/shared-schemas/dist/realtime-api.schema.d.ts.map +1 -1
- package/node_modules/@insforge/shared-schemas/dist/realtime-api.schema.js +5 -1
- package/node_modules/@insforge/shared-schemas/dist/realtime-api.schema.js.map +1 -1
- package/node_modules/@insforge/shared-schemas/dist/realtime.schema.d.ts +12 -4
- package/node_modules/@insforge/shared-schemas/dist/realtime.schema.d.ts.map +1 -1
- package/node_modules/@insforge/shared-schemas/dist/realtime.schema.js +6 -0
- package/node_modules/@insforge/shared-schemas/dist/realtime.schema.js.map +1 -1
- package/node_modules/@insforge/shared-schemas/dist/schedules-api.schema.d.ts +287 -0
- package/node_modules/@insforge/shared-schemas/dist/schedules-api.schema.d.ts.map +1 -0
- package/node_modules/@insforge/shared-schemas/dist/schedules-api.schema.js +81 -0
- package/node_modules/@insforge/shared-schemas/dist/schedules-api.schema.js.map +1 -0
- package/node_modules/@insforge/shared-schemas/dist/schedules.schema.d.ts +77 -0
- package/node_modules/@insforge/shared-schemas/dist/schedules.schema.d.ts.map +1 -0
- package/node_modules/@insforge/shared-schemas/dist/schedules.schema.js +36 -0
- package/node_modules/@insforge/shared-schemas/dist/schedules.schema.js.map +1 -0
- package/node_modules/@insforge/shared-schemas/dist/secrets-api.schema.d.ts +113 -0
- package/node_modules/@insforge/shared-schemas/dist/secrets-api.schema.d.ts.map +1 -0
- package/node_modules/@insforge/shared-schemas/dist/secrets-api.schema.js +31 -0
- package/node_modules/@insforge/shared-schemas/dist/secrets-api.schema.js.map +1 -0
- package/node_modules/@insforge/shared-schemas/dist/secrets.schema.d.ts +31 -0
- package/node_modules/@insforge/shared-schemas/dist/secrets.schema.d.ts.map +1 -0
- package/node_modules/@insforge/shared-schemas/dist/secrets.schema.js +13 -0
- package/node_modules/@insforge/shared-schemas/dist/secrets.schema.js.map +1 -0
- package/node_modules/@insforge/shared-schemas/dist/storage-api.schema.d.ts +27 -2
- package/node_modules/@insforge/shared-schemas/dist/storage-api.schema.d.ts.map +1 -1
- package/node_modules/@insforge/shared-schemas/dist/storage-api.schema.js +9 -1
- package/node_modules/@insforge/shared-schemas/dist/storage-api.schema.js.map +1 -1
- package/node_modules/@insforge/shared-schemas/dist/storage.schema.d.ts +17 -0
- package/node_modules/@insforge/shared-schemas/dist/storage.schema.d.ts.map +1 -1
- package/node_modules/@insforge/shared-schemas/dist/storage.schema.js +6 -0
- package/node_modules/@insforge/shared-schemas/dist/storage.schema.js.map +1 -1
- package/node_modules/@insforge/shared-schemas/package.json +2 -1
- package/package.json +5 -6
- package/src/cli.js +2 -2
- package/src/commands/init.js +20 -362
- package/src/commands/status.js +58 -51
- package/src/commands/sync.js +37 -25
- package/src/commands/uninstall.js +121 -104
- package/src/lib/claude-config.js +130 -35
- package/src/lib/diagnostics.js +88 -57
- package/src/lib/doctor.js +50 -0
- package/src/lib/insforge-client.js +13 -9
- package/src/lib/integrations/claude.js +106 -0
- package/src/lib/integrations/codex.js +88 -0
- package/src/lib/integrations/context.js +76 -0
- package/src/lib/integrations/every-code.js +88 -0
- package/src/lib/integrations/gemini.js +86 -0
- package/src/lib/integrations/index.js +85 -0
- package/src/lib/integrations/openclaw-legacy.js +123 -0
- package/src/lib/integrations/openclaw-session.js +132 -0
- package/src/lib/integrations/opencode.js +86 -0
- package/src/lib/integrations/utils.js +39 -0
- package/src/lib/opencode-sqlite.js +113 -0
- package/src/lib/opencode-usage-audit.js +3 -2
- package/src/lib/rollout.js +227 -1
- package/src/lib/runtime-config.js +7 -5
- package/src/lib/vibeusage-api.js +11 -7
- package/src/shared/copy-registry.cjs +142 -0
- package/src/shared/copy-registry.cjs.d.ts +33 -0
- package/src/shared/runtime-defaults.cjs +11 -0
- package/src/shared/runtime-defaults.cjs.d.ts +3 -0
- package/src/shared/vibeusage-function-contract.cjs +34 -0
- package/src/shared/vibeusage-function-contract.cjs.d.ts +4 -0
- package/src/commands/activate-if-needed.js +0 -41
- package/src/lib/activation-check.js +0 -341
|
@@ -0,0 +1,86 @@
|
|
|
1
|
+
const {
|
|
2
|
+
isGeminiHookConfigured,
|
|
3
|
+
upsertGeminiHook,
|
|
4
|
+
removeGeminiHook,
|
|
5
|
+
} = require("../gemini-config");
|
|
6
|
+
const { isDir, isFile } = require("./utils");
|
|
7
|
+
|
|
8
|
+
module.exports = {
|
|
9
|
+
name: "gemini",
|
|
10
|
+
summaryLabel: "Gemini",
|
|
11
|
+
statusLabel: "Gemini hooks",
|
|
12
|
+
async probe(ctx) {
|
|
13
|
+
const hasConfigDir = await isDir(ctx.gemini.configDir);
|
|
14
|
+
if (!hasConfigDir) {
|
|
15
|
+
return baseProbe(this, { status: "not_installed", detail: "Config not found" });
|
|
16
|
+
}
|
|
17
|
+
const configured = await isGeminiHookConfigured({
|
|
18
|
+
settingsPath: ctx.gemini.settingsPath,
|
|
19
|
+
hookCommand: ctx.gemini.hookCommand,
|
|
20
|
+
});
|
|
21
|
+
return baseProbe(this, {
|
|
22
|
+
status: configured ? "ready" : "drifted",
|
|
23
|
+
detail: configured ? "Hooks installed" : "Run vibeusage init to reconcile hooks",
|
|
24
|
+
configured,
|
|
25
|
+
});
|
|
26
|
+
},
|
|
27
|
+
async install(ctx) {
|
|
28
|
+
if (!(await isDir(ctx.gemini.configDir))) {
|
|
29
|
+
return action(this, "skipped", false, "Config not found");
|
|
30
|
+
}
|
|
31
|
+
const result = await upsertGeminiHook({
|
|
32
|
+
settingsPath: ctx.gemini.settingsPath,
|
|
33
|
+
hookCommand: ctx.gemini.hookCommand,
|
|
34
|
+
});
|
|
35
|
+
return action(
|
|
36
|
+
this,
|
|
37
|
+
result.changed ? "installed" : "set",
|
|
38
|
+
Boolean(result.changed),
|
|
39
|
+
result.changed ? "Hooks installed" : "Hooks already installed",
|
|
40
|
+
);
|
|
41
|
+
},
|
|
42
|
+
async uninstall(ctx) {
|
|
43
|
+
if (!(await isDir(ctx.gemini.configDir))) {
|
|
44
|
+
return action(this, "skipped", false, "config dir not found");
|
|
45
|
+
}
|
|
46
|
+
const result = await removeGeminiHook({
|
|
47
|
+
settingsPath: ctx.gemini.settingsPath,
|
|
48
|
+
hookCommand: ctx.gemini.hookCommand,
|
|
49
|
+
});
|
|
50
|
+
if (result.removed) {
|
|
51
|
+
return action(this, "removed", true, ctx.gemini.settingsPath);
|
|
52
|
+
}
|
|
53
|
+
if (result.skippedReason === "hook-missing") {
|
|
54
|
+
return action(this, "unchanged", false, "no change", {
|
|
55
|
+
skippedReason: result.skippedReason,
|
|
56
|
+
});
|
|
57
|
+
}
|
|
58
|
+
return action(this, "skipped", false, "settings.json not found");
|
|
59
|
+
},
|
|
60
|
+
renderStatusValue(probe) {
|
|
61
|
+
if (probe.status === "ready") return "set";
|
|
62
|
+
if (probe.status === "not_installed") return "unset";
|
|
63
|
+
return probe.status;
|
|
64
|
+
},
|
|
65
|
+
};
|
|
66
|
+
|
|
67
|
+
function baseProbe(descriptor, values) {
|
|
68
|
+
return {
|
|
69
|
+
name: descriptor.name,
|
|
70
|
+
summaryLabel: descriptor.summaryLabel,
|
|
71
|
+
statusLabel: descriptor.statusLabel,
|
|
72
|
+
configured: false,
|
|
73
|
+
...values,
|
|
74
|
+
};
|
|
75
|
+
}
|
|
76
|
+
|
|
77
|
+
function action(descriptor, status, changed, detail, extras = {}) {
|
|
78
|
+
return {
|
|
79
|
+
name: descriptor.name,
|
|
80
|
+
label: descriptor.summaryLabel,
|
|
81
|
+
status,
|
|
82
|
+
changed,
|
|
83
|
+
detail,
|
|
84
|
+
...extras,
|
|
85
|
+
};
|
|
86
|
+
}
|
|
@@ -0,0 +1,85 @@
|
|
|
1
|
+
const { createIntegrationContext } = require("./context");
|
|
2
|
+
const codex = require("./codex");
|
|
3
|
+
const everyCode = require("./every-code");
|
|
4
|
+
const claude = require("./claude");
|
|
5
|
+
const gemini = require("./gemini");
|
|
6
|
+
const opencode = require("./opencode");
|
|
7
|
+
const openclawSession = require("./openclaw-session");
|
|
8
|
+
const openclawLegacy = require("./openclaw-legacy");
|
|
9
|
+
|
|
10
|
+
const INTEGRATIONS = [
|
|
11
|
+
codex,
|
|
12
|
+
everyCode,
|
|
13
|
+
claude,
|
|
14
|
+
gemini,
|
|
15
|
+
opencode,
|
|
16
|
+
openclawSession,
|
|
17
|
+
openclawLegacy,
|
|
18
|
+
];
|
|
19
|
+
|
|
20
|
+
function listIntegrations() {
|
|
21
|
+
return INTEGRATIONS.slice();
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
async function probeIntegrations(context) {
|
|
25
|
+
const ctx = await ensureContext(context);
|
|
26
|
+
const results = [];
|
|
27
|
+
for (const integration of INTEGRATIONS) {
|
|
28
|
+
results.push(await integration.probe(ctx));
|
|
29
|
+
}
|
|
30
|
+
return results;
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
async function installIntegrations(context) {
|
|
34
|
+
const ctx = await ensureContext(context);
|
|
35
|
+
const results = [];
|
|
36
|
+
for (const integration of INTEGRATIONS) {
|
|
37
|
+
results.push(await integration.install(ctx));
|
|
38
|
+
}
|
|
39
|
+
return results;
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
async function uninstallIntegrations(context) {
|
|
43
|
+
const ctx = await ensureContext(context);
|
|
44
|
+
const results = [];
|
|
45
|
+
for (const integration of INTEGRATIONS) {
|
|
46
|
+
results.push(await integration.uninstall(ctx));
|
|
47
|
+
}
|
|
48
|
+
return results;
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
function summarizeProbeForInitPreview(probe) {
|
|
52
|
+
switch (probe.status) {
|
|
53
|
+
case "ready":
|
|
54
|
+
return { label: probe.summaryLabel, status: "set", detail: "Already configured" };
|
|
55
|
+
case "not_installed":
|
|
56
|
+
if (probe.initPreviewStatus) {
|
|
57
|
+
return {
|
|
58
|
+
label: probe.summaryLabel,
|
|
59
|
+
status: probe.initPreviewStatus,
|
|
60
|
+
detail: probe.initPreviewDetail || probe.detail,
|
|
61
|
+
};
|
|
62
|
+
}
|
|
63
|
+
return { label: probe.summaryLabel, status: "skipped", detail: probe.detail };
|
|
64
|
+
case "unsupported_legacy":
|
|
65
|
+
return { label: probe.summaryLabel, status: "updated", detail: "Will replace legacy config" };
|
|
66
|
+
case "unreadable":
|
|
67
|
+
return { label: probe.summaryLabel, status: "skipped", detail: probe.detail || "Config unreadable" };
|
|
68
|
+
default:
|
|
69
|
+
return { label: probe.summaryLabel, status: "updated", detail: "Will reconcile config" };
|
|
70
|
+
}
|
|
71
|
+
}
|
|
72
|
+
|
|
73
|
+
async function ensureContext(context) {
|
|
74
|
+
if (context?.trackerPaths && context?.notifyPath) return context;
|
|
75
|
+
return createIntegrationContext(context);
|
|
76
|
+
}
|
|
77
|
+
|
|
78
|
+
module.exports = {
|
|
79
|
+
listIntegrations,
|
|
80
|
+
createIntegrationContext,
|
|
81
|
+
probeIntegrations,
|
|
82
|
+
installIntegrations,
|
|
83
|
+
uninstallIntegrations,
|
|
84
|
+
summarizeProbeForInitPreview,
|
|
85
|
+
};
|
|
@@ -0,0 +1,123 @@
|
|
|
1
|
+
const { probeOpenclawHookState, removeOpenclawHookConfig } = require("../openclaw-hook");
|
|
2
|
+
|
|
3
|
+
module.exports = {
|
|
4
|
+
name: "openclaw-legacy",
|
|
5
|
+
summaryLabel: "OpenClaw Hook (legacy)",
|
|
6
|
+
statusLabel: "OpenClaw hook (legacy)",
|
|
7
|
+
async probe(ctx) {
|
|
8
|
+
const state = await probeOpenclawHookState({
|
|
9
|
+
home: ctx.home,
|
|
10
|
+
trackerDir: ctx.trackerPaths.trackerDir,
|
|
11
|
+
env: ctx.env,
|
|
12
|
+
});
|
|
13
|
+
if (state?.skippedReason === "openclaw-config-missing") {
|
|
14
|
+
return baseProbe(this, { status: "not_installed", detail: "OpenClaw config not found" });
|
|
15
|
+
}
|
|
16
|
+
if (state?.skippedReason === "openclaw-config-unreadable") {
|
|
17
|
+
return baseProbe(this, {
|
|
18
|
+
status: "unreadable",
|
|
19
|
+
detail: state.error
|
|
20
|
+
? `OpenClaw config unreadable: ${state.error}`
|
|
21
|
+
: "OpenClaw config unreadable",
|
|
22
|
+
linked: Boolean(state.linked),
|
|
23
|
+
enabled: Boolean(state.enabled),
|
|
24
|
+
});
|
|
25
|
+
}
|
|
26
|
+
if (state?.configured || state?.linked || state?.enabled) {
|
|
27
|
+
return baseProbe(this, {
|
|
28
|
+
status: "unsupported_legacy",
|
|
29
|
+
detail: "Legacy OpenClaw hook detected; run vibeusage init",
|
|
30
|
+
linked: Boolean(state.linked),
|
|
31
|
+
enabled: Boolean(state.enabled),
|
|
32
|
+
});
|
|
33
|
+
}
|
|
34
|
+
return baseProbe(this, { status: "not_installed", detail: "Legacy hook not installed" });
|
|
35
|
+
},
|
|
36
|
+
async install(ctx) {
|
|
37
|
+
const state = await probeOpenclawHookState({
|
|
38
|
+
home: ctx.home,
|
|
39
|
+
trackerDir: ctx.trackerPaths.trackerDir,
|
|
40
|
+
env: ctx.env,
|
|
41
|
+
});
|
|
42
|
+
if (state?.skippedReason === "openclaw-config-unreadable") {
|
|
43
|
+
return action(
|
|
44
|
+
this,
|
|
45
|
+
"skipped",
|
|
46
|
+
false,
|
|
47
|
+
state.error ? `OpenClaw config unreadable: ${state.error}` : "OpenClaw config unreadable",
|
|
48
|
+
{ skippedReason: state.skippedReason },
|
|
49
|
+
);
|
|
50
|
+
}
|
|
51
|
+
if (!(state?.configured || state?.linked || state?.enabled)) {
|
|
52
|
+
return action(this, "unchanged", false, "no change");
|
|
53
|
+
}
|
|
54
|
+
const result = await removeOpenclawHookConfig({
|
|
55
|
+
home: ctx.home,
|
|
56
|
+
trackerDir: ctx.trackerPaths.trackerDir,
|
|
57
|
+
env: ctx.env,
|
|
58
|
+
});
|
|
59
|
+
if (result?.removed) {
|
|
60
|
+
return action(this, "updated", true, "Removed legacy command hook");
|
|
61
|
+
}
|
|
62
|
+
if (result?.skippedReason === "openclaw-config-unreadable") {
|
|
63
|
+
return action(
|
|
64
|
+
this,
|
|
65
|
+
"skipped",
|
|
66
|
+
false,
|
|
67
|
+
result.error ? `OpenClaw config unreadable: ${result.error}` : "OpenClaw config unreadable",
|
|
68
|
+
{ skippedReason: result.skippedReason },
|
|
69
|
+
);
|
|
70
|
+
}
|
|
71
|
+
return action(this, "unchanged", false, "no change");
|
|
72
|
+
},
|
|
73
|
+
async uninstall(ctx) {
|
|
74
|
+
const result = await removeOpenclawHookConfig({
|
|
75
|
+
home: ctx.home,
|
|
76
|
+
trackerDir: ctx.trackerPaths.trackerDir,
|
|
77
|
+
env: ctx.env,
|
|
78
|
+
});
|
|
79
|
+
if (result?.removed) {
|
|
80
|
+
return action(this, "removed", true, result.openclawConfigPath);
|
|
81
|
+
}
|
|
82
|
+
if (result?.skippedReason === "openclaw-config-missing") {
|
|
83
|
+
return action(this, "skipped", false, "openclaw config not found", {
|
|
84
|
+
skippedReason: result.skippedReason,
|
|
85
|
+
});
|
|
86
|
+
}
|
|
87
|
+
if (result?.skippedReason === "openclaw-config-unreadable") {
|
|
88
|
+
return action(
|
|
89
|
+
this,
|
|
90
|
+
"skipped",
|
|
91
|
+
false,
|
|
92
|
+
result.error ? `openclaw config unreadable: ${result.error}` : "openclaw config unreadable",
|
|
93
|
+
{ skippedReason: result.skippedReason },
|
|
94
|
+
);
|
|
95
|
+
}
|
|
96
|
+
return action(this, "unchanged", false, "no change");
|
|
97
|
+
},
|
|
98
|
+
renderStatusValue(probe) {
|
|
99
|
+
if (probe.status === "not_installed") return "unset";
|
|
100
|
+
return probe.status;
|
|
101
|
+
},
|
|
102
|
+
};
|
|
103
|
+
|
|
104
|
+
function baseProbe(descriptor, values) {
|
|
105
|
+
return {
|
|
106
|
+
name: descriptor.name,
|
|
107
|
+
summaryLabel: descriptor.summaryLabel,
|
|
108
|
+
statusLabel: descriptor.statusLabel,
|
|
109
|
+
configured: false,
|
|
110
|
+
...values,
|
|
111
|
+
};
|
|
112
|
+
}
|
|
113
|
+
|
|
114
|
+
function action(descriptor, status, changed, detail, extras = {}) {
|
|
115
|
+
return {
|
|
116
|
+
name: descriptor.name,
|
|
117
|
+
label: descriptor.summaryLabel,
|
|
118
|
+
status,
|
|
119
|
+
changed,
|
|
120
|
+
detail,
|
|
121
|
+
...extras,
|
|
122
|
+
};
|
|
123
|
+
}
|
|
@@ -0,0 +1,132 @@
|
|
|
1
|
+
const {
|
|
2
|
+
installOpenclawSessionPlugin,
|
|
3
|
+
probeOpenclawSessionPluginState,
|
|
4
|
+
removeOpenclawSessionPluginConfig,
|
|
5
|
+
} = require("../openclaw-session-plugin");
|
|
6
|
+
|
|
7
|
+
module.exports = {
|
|
8
|
+
name: "openclaw-session",
|
|
9
|
+
summaryLabel: "OpenClaw Session Plugin",
|
|
10
|
+
statusLabel: "OpenClaw session plugin",
|
|
11
|
+
async probe(ctx) {
|
|
12
|
+
const state = await probeOpenclawSessionPluginState({
|
|
13
|
+
home: ctx.home,
|
|
14
|
+
trackerDir: ctx.trackerPaths.trackerDir,
|
|
15
|
+
env: ctx.env,
|
|
16
|
+
});
|
|
17
|
+
if (state?.skippedReason === "openclaw-config-missing") {
|
|
18
|
+
return baseProbe(this, { status: "not_installed", detail: "OpenClaw config not found" });
|
|
19
|
+
}
|
|
20
|
+
if (state?.skippedReason === "openclaw-config-unreadable") {
|
|
21
|
+
return baseProbe(this, {
|
|
22
|
+
status: "unreadable",
|
|
23
|
+
detail: state.error
|
|
24
|
+
? `OpenClaw config unreadable: ${state.error}`
|
|
25
|
+
: "OpenClaw config unreadable",
|
|
26
|
+
linked: Boolean(state.linked),
|
|
27
|
+
enabled: Boolean(state.enabled),
|
|
28
|
+
});
|
|
29
|
+
}
|
|
30
|
+
if (state?.configured) {
|
|
31
|
+
return baseProbe(this, {
|
|
32
|
+
status: "ready",
|
|
33
|
+
detail: "Session plugin linked",
|
|
34
|
+
configured: true,
|
|
35
|
+
linked: Boolean(state.linked),
|
|
36
|
+
enabled: Boolean(state.enabled),
|
|
37
|
+
});
|
|
38
|
+
}
|
|
39
|
+
return baseProbe(this, {
|
|
40
|
+
status: "drifted",
|
|
41
|
+
detail: "Run vibeusage init to reconcile session plugin",
|
|
42
|
+
linked: Boolean(state?.linked),
|
|
43
|
+
enabled: Boolean(state?.enabled),
|
|
44
|
+
});
|
|
45
|
+
},
|
|
46
|
+
async install(ctx) {
|
|
47
|
+
const before = await probeOpenclawSessionPluginState({
|
|
48
|
+
home: ctx.home,
|
|
49
|
+
trackerDir: ctx.trackerPaths.trackerDir,
|
|
50
|
+
env: ctx.env,
|
|
51
|
+
});
|
|
52
|
+
const result = await installOpenclawSessionPlugin({
|
|
53
|
+
home: ctx.home,
|
|
54
|
+
trackerDir: ctx.trackerPaths.trackerDir,
|
|
55
|
+
packageName: "vibeusage",
|
|
56
|
+
env: ctx.env,
|
|
57
|
+
});
|
|
58
|
+
if (result?.skippedReason === "openclaw-cli-missing") {
|
|
59
|
+
return action(this, "skipped", false, "OpenClaw CLI not found");
|
|
60
|
+
}
|
|
61
|
+
if (result?.skippedReason === "openclaw-plugins-install-failed") {
|
|
62
|
+
return action(this, "skipped", false, `Install failed: ${result.error || "unknown error"}`);
|
|
63
|
+
}
|
|
64
|
+
if (result?.skippedReason === "openclaw-config-unreadable") {
|
|
65
|
+
return action(
|
|
66
|
+
this,
|
|
67
|
+
"skipped",
|
|
68
|
+
false,
|
|
69
|
+
result.error ? `OpenClaw config unreadable: ${result.error}` : "OpenClaw config unreadable",
|
|
70
|
+
);
|
|
71
|
+
}
|
|
72
|
+
return action(
|
|
73
|
+
this,
|
|
74
|
+
before?.configured ? "set" : "installed",
|
|
75
|
+
Boolean(!before?.configured && result?.configured),
|
|
76
|
+
before?.configured
|
|
77
|
+
? "Session plugin already linked"
|
|
78
|
+
: "Session plugin linked (restart OpenClaw gateway to activate)",
|
|
79
|
+
);
|
|
80
|
+
},
|
|
81
|
+
async uninstall(ctx) {
|
|
82
|
+
const result = await removeOpenclawSessionPluginConfig({
|
|
83
|
+
home: ctx.home,
|
|
84
|
+
trackerDir: ctx.trackerPaths.trackerDir,
|
|
85
|
+
env: ctx.env,
|
|
86
|
+
});
|
|
87
|
+
if (result?.removed) {
|
|
88
|
+
return action(this, "removed", true, result.openclawConfigPath);
|
|
89
|
+
}
|
|
90
|
+
if (result?.skippedReason === "openclaw-config-missing") {
|
|
91
|
+
return action(this, "skipped", false, "openclaw config not found", {
|
|
92
|
+
skippedReason: result.skippedReason,
|
|
93
|
+
});
|
|
94
|
+
}
|
|
95
|
+
if (result?.skippedReason === "openclaw-config-unreadable") {
|
|
96
|
+
return action(
|
|
97
|
+
this,
|
|
98
|
+
"skipped",
|
|
99
|
+
false,
|
|
100
|
+
result.error ? `openclaw config unreadable: ${result.error}` : "openclaw config unreadable",
|
|
101
|
+
{ skippedReason: result.skippedReason },
|
|
102
|
+
);
|
|
103
|
+
}
|
|
104
|
+
return action(this, "unchanged", false, "no change");
|
|
105
|
+
},
|
|
106
|
+
renderStatusValue(probe) {
|
|
107
|
+
if (probe.status === "ready") return "set";
|
|
108
|
+
if (probe.status === "not_installed") return "unset";
|
|
109
|
+
return probe.status;
|
|
110
|
+
},
|
|
111
|
+
};
|
|
112
|
+
|
|
113
|
+
function baseProbe(descriptor, values) {
|
|
114
|
+
return {
|
|
115
|
+
name: descriptor.name,
|
|
116
|
+
summaryLabel: descriptor.summaryLabel,
|
|
117
|
+
statusLabel: descriptor.statusLabel,
|
|
118
|
+
configured: false,
|
|
119
|
+
...values,
|
|
120
|
+
};
|
|
121
|
+
}
|
|
122
|
+
|
|
123
|
+
function action(descriptor, status, changed, detail, extras = {}) {
|
|
124
|
+
return {
|
|
125
|
+
name: descriptor.name,
|
|
126
|
+
label: descriptor.summaryLabel,
|
|
127
|
+
status,
|
|
128
|
+
changed,
|
|
129
|
+
detail,
|
|
130
|
+
...extras,
|
|
131
|
+
};
|
|
132
|
+
}
|
|
@@ -0,0 +1,86 @@
|
|
|
1
|
+
const { isOpencodePluginInstalled, upsertOpencodePlugin, removeOpencodePlugin } = require("../opencode-config");
|
|
2
|
+
const { isDir } = require("./utils");
|
|
3
|
+
|
|
4
|
+
module.exports = {
|
|
5
|
+
name: "opencode",
|
|
6
|
+
summaryLabel: "Opencode Plugin",
|
|
7
|
+
statusLabel: "Opencode plugin",
|
|
8
|
+
async probe(ctx) {
|
|
9
|
+
const hasConfigDir = await isDir(ctx.opencode.configDir);
|
|
10
|
+
if (!hasConfigDir) {
|
|
11
|
+
return baseProbe(this, {
|
|
12
|
+
status: "not_installed",
|
|
13
|
+
detail: "Config not found",
|
|
14
|
+
initPreviewStatus: "updated",
|
|
15
|
+
initPreviewDetail: "Will install plugin",
|
|
16
|
+
});
|
|
17
|
+
}
|
|
18
|
+
const configured = await isOpencodePluginInstalled({ configDir: ctx.opencode.configDir });
|
|
19
|
+
return baseProbe(this, {
|
|
20
|
+
status: configured ? "ready" : "drifted",
|
|
21
|
+
detail: configured ? "Plugin installed" : "Run vibeusage init to reconcile plugin",
|
|
22
|
+
configured,
|
|
23
|
+
});
|
|
24
|
+
},
|
|
25
|
+
async install(ctx) {
|
|
26
|
+
const result = await upsertOpencodePlugin({
|
|
27
|
+
configDir: ctx.opencode.configDir,
|
|
28
|
+
notifyPath: ctx.notifyPath,
|
|
29
|
+
});
|
|
30
|
+
if (result?.skippedReason === "config-missing") {
|
|
31
|
+
return action(this, "skipped", false, "Config not found");
|
|
32
|
+
}
|
|
33
|
+
return action(
|
|
34
|
+
this,
|
|
35
|
+
result.changed ? "installed" : "set",
|
|
36
|
+
Boolean(result.changed),
|
|
37
|
+
result.changed ? "Plugin installed" : "Plugin already installed",
|
|
38
|
+
);
|
|
39
|
+
},
|
|
40
|
+
async uninstall(ctx) {
|
|
41
|
+
if (!(await isDir(ctx.opencode.configDir))) {
|
|
42
|
+
return action(this, "skipped", false, "config dir not found");
|
|
43
|
+
}
|
|
44
|
+
const result = await removeOpencodePlugin({ configDir: ctx.opencode.configDir });
|
|
45
|
+
if (result.removed) {
|
|
46
|
+
return action(this, "removed", true, ctx.opencode.configDir);
|
|
47
|
+
}
|
|
48
|
+
if (result.skippedReason === "plugin-missing") {
|
|
49
|
+
return action(this, "unchanged", false, "no change", {
|
|
50
|
+
skippedReason: result.skippedReason,
|
|
51
|
+
});
|
|
52
|
+
}
|
|
53
|
+
if (result.skippedReason === "unexpected-content") {
|
|
54
|
+
return action(this, "skipped", false, "unexpected content", {
|
|
55
|
+
skippedReason: result.skippedReason,
|
|
56
|
+
});
|
|
57
|
+
}
|
|
58
|
+
return action(this, "skipped", false, "config dir not found");
|
|
59
|
+
},
|
|
60
|
+
renderStatusValue(probe) {
|
|
61
|
+
if (probe.status === "ready") return "set";
|
|
62
|
+
if (probe.status === "not_installed") return "unset";
|
|
63
|
+
return probe.status;
|
|
64
|
+
},
|
|
65
|
+
};
|
|
66
|
+
|
|
67
|
+
function baseProbe(descriptor, values) {
|
|
68
|
+
return {
|
|
69
|
+
name: descriptor.name,
|
|
70
|
+
summaryLabel: descriptor.summaryLabel,
|
|
71
|
+
statusLabel: descriptor.statusLabel,
|
|
72
|
+
configured: false,
|
|
73
|
+
...values,
|
|
74
|
+
};
|
|
75
|
+
}
|
|
76
|
+
|
|
77
|
+
function action(descriptor, status, changed, detail, extras = {}) {
|
|
78
|
+
return {
|
|
79
|
+
name: descriptor.name,
|
|
80
|
+
label: descriptor.summaryLabel,
|
|
81
|
+
status,
|
|
82
|
+
changed,
|
|
83
|
+
detail,
|
|
84
|
+
...extras,
|
|
85
|
+
};
|
|
86
|
+
}
|
|
@@ -0,0 +1,39 @@
|
|
|
1
|
+
const fs = require("node:fs/promises");
|
|
2
|
+
|
|
3
|
+
async function isFile(targetPath) {
|
|
4
|
+
try {
|
|
5
|
+
const stat = await fs.stat(targetPath);
|
|
6
|
+
return stat.isFile();
|
|
7
|
+
} catch (_err) {
|
|
8
|
+
return false;
|
|
9
|
+
}
|
|
10
|
+
}
|
|
11
|
+
|
|
12
|
+
async function isDir(targetPath) {
|
|
13
|
+
try {
|
|
14
|
+
const stat = await fs.stat(targetPath);
|
|
15
|
+
return stat.isDirectory();
|
|
16
|
+
} catch (_err) {
|
|
17
|
+
return false;
|
|
18
|
+
}
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
function arraysEqual(left, right) {
|
|
22
|
+
if (!Array.isArray(left) || !Array.isArray(right)) return false;
|
|
23
|
+
if (left.length !== right.length) return false;
|
|
24
|
+
for (let i = 0; i < left.length; i += 1) {
|
|
25
|
+
if (String(left[i]) !== String(right[i])) return false;
|
|
26
|
+
}
|
|
27
|
+
return true;
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
function findIntegration(results, name) {
|
|
31
|
+
return Array.isArray(results) ? results.find((entry) => entry?.name === name) || null : null;
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
module.exports = {
|
|
35
|
+
isFile,
|
|
36
|
+
isDir,
|
|
37
|
+
arraysEqual,
|
|
38
|
+
findIntegration,
|
|
39
|
+
};
|
|
@@ -0,0 +1,113 @@
|
|
|
1
|
+
const fs = require("node:fs/promises");
|
|
2
|
+
const cp = require("node:child_process");
|
|
3
|
+
const { promisify } = require("node:util");
|
|
4
|
+
|
|
5
|
+
const execFileAsync = promisify(cp.execFile);
|
|
6
|
+
|
|
7
|
+
function buildMessageQuery(lastTimeCreated) {
|
|
8
|
+
const since = Number.isFinite(lastTimeCreated) ? Math.max(0, Math.trunc(lastTimeCreated)) : 0;
|
|
9
|
+
return [
|
|
10
|
+
"SELECT json_object(",
|
|
11
|
+
"'id', m.id,",
|
|
12
|
+
"'session_id', m.session_id,",
|
|
13
|
+
"'time_created', m.time_created,",
|
|
14
|
+
"'role', json_extract(m.data, '$.role'),",
|
|
15
|
+
"'project_worktree', p.worktree,",
|
|
16
|
+
"'data', m.data",
|
|
17
|
+
")",
|
|
18
|
+
"FROM message m",
|
|
19
|
+
"LEFT JOIN session s ON s.id = m.session_id",
|
|
20
|
+
"LEFT JOIN project p ON p.id = s.project_id",
|
|
21
|
+
`WHERE m.time_created >= ${since}`,
|
|
22
|
+
"ORDER BY m.time_created ASC;",
|
|
23
|
+
].join(" ");
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
function parseMessageRows(stdout) {
|
|
27
|
+
const lines = String(stdout || "")
|
|
28
|
+
.split(/\r?\n/)
|
|
29
|
+
.map((line) => line.trim())
|
|
30
|
+
.filter(Boolean);
|
|
31
|
+
|
|
32
|
+
const rows = [];
|
|
33
|
+
for (const line of lines) {
|
|
34
|
+
try {
|
|
35
|
+
const parsed = JSON.parse(line);
|
|
36
|
+
if (!parsed || typeof parsed !== "object") continue;
|
|
37
|
+
rows.push(parsed);
|
|
38
|
+
} catch (_) {}
|
|
39
|
+
}
|
|
40
|
+
return rows;
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
function normalizeErrorCode(error) {
|
|
44
|
+
return typeof error?.code === "string" && error.code.trim() ? error.code.trim() : null;
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
async function readOpencodeSqliteRows({
|
|
48
|
+
dbPath,
|
|
49
|
+
lastTimeCreated = 0,
|
|
50
|
+
expectedInode = 0,
|
|
51
|
+
statFn = fs.stat,
|
|
52
|
+
execFileFn = execFileAsync,
|
|
53
|
+
}) {
|
|
54
|
+
const checkedAt = new Date().toISOString();
|
|
55
|
+
const st = await statFn(dbPath).catch(() => null);
|
|
56
|
+
if (!st || !st.isFile()) {
|
|
57
|
+
return {
|
|
58
|
+
status: "missing-db",
|
|
59
|
+
checkedAt,
|
|
60
|
+
rows: [],
|
|
61
|
+
inode: 0,
|
|
62
|
+
cursorReset: false,
|
|
63
|
+
errorCode: null,
|
|
64
|
+
};
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
try {
|
|
68
|
+
const inode = Number.isFinite(st.ino) ? st.ino : 0;
|
|
69
|
+
const effectiveLastTimeCreated =
|
|
70
|
+
expectedInode && inode && expectedInode !== inode ? 0 : lastTimeCreated;
|
|
71
|
+
const { stdout } = await execFileFn(
|
|
72
|
+
"sqlite3",
|
|
73
|
+
["-readonly", dbPath, buildMessageQuery(effectiveLastTimeCreated)],
|
|
74
|
+
{ maxBuffer: 64 * 1024 * 1024 },
|
|
75
|
+
);
|
|
76
|
+
return {
|
|
77
|
+
status: "ok",
|
|
78
|
+
checkedAt,
|
|
79
|
+
rows: parseMessageRows(stdout),
|
|
80
|
+
inode,
|
|
81
|
+
cursorReset: Boolean(expectedInode && inode && expectedInode !== inode),
|
|
82
|
+
errorCode: null,
|
|
83
|
+
};
|
|
84
|
+
} catch (error) {
|
|
85
|
+
const errorCode = normalizeErrorCode(error);
|
|
86
|
+
if (error && error.code === "ENOENT") {
|
|
87
|
+
return {
|
|
88
|
+
status: "missing-sqlite3",
|
|
89
|
+
checkedAt,
|
|
90
|
+
rows: [],
|
|
91
|
+
inode: Number.isFinite(st.ino) ? st.ino : 0,
|
|
92
|
+
cursorReset: false,
|
|
93
|
+
error,
|
|
94
|
+
errorCode,
|
|
95
|
+
};
|
|
96
|
+
}
|
|
97
|
+
return {
|
|
98
|
+
status: "query-failed",
|
|
99
|
+
checkedAt,
|
|
100
|
+
rows: [],
|
|
101
|
+
inode: Number.isFinite(st.ino) ? st.ino : 0,
|
|
102
|
+
cursorReset: false,
|
|
103
|
+
error,
|
|
104
|
+
errorCode,
|
|
105
|
+
};
|
|
106
|
+
}
|
|
107
|
+
}
|
|
108
|
+
|
|
109
|
+
module.exports = {
|
|
110
|
+
buildMessageQuery,
|
|
111
|
+
parseMessageRows,
|
|
112
|
+
readOpencodeSqliteRows,
|
|
113
|
+
};
|
|
@@ -32,13 +32,14 @@ function addTotals(target, delta) {
|
|
|
32
32
|
|
|
33
33
|
async function buildLocalHourlyTotals({ storageDir, source = "opencode" }) {
|
|
34
34
|
const messageFiles = await listOpencodeMessageFiles(storageDir);
|
|
35
|
+
const opencodeDbPath = path.resolve(storageDir, "..", "opencode.db");
|
|
35
36
|
const queuePath = path.join(
|
|
36
37
|
os.tmpdir(),
|
|
37
38
|
`vibeusage-opencode-audit-${process.pid}-${Date.now()}.jsonl`,
|
|
38
39
|
);
|
|
39
|
-
const cursors = { version: 1, files: {}, hourly: null, opencode: null };
|
|
40
|
+
const cursors = { version: 1, files: {}, hourly: null, opencode: null, opencodeSqlite: null };
|
|
40
41
|
|
|
41
|
-
await parseOpencodeIncremental({ messageFiles, cursors, queuePath, source });
|
|
42
|
+
await parseOpencodeIncremental({ messageFiles, opencodeDbPath, cursors, queuePath, source });
|
|
42
43
|
await fs.rm(queuePath, { force: true }).catch(() => {});
|
|
43
44
|
|
|
44
45
|
const byHour = new Map();
|