@gurulu/cli 1.2.2 → 1.4.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/dist/bin.js +207 -79
- package/dist/index.js +207 -79
- package/dist/lib/detect.d.ts +5 -0
- package/dist/lib/detect.d.ts.map +1 -1
- package/dist/lib/detect.js +22 -1
- package/dist/lib/install-plan.d.ts +6 -0
- package/dist/lib/install-plan.d.ts.map +1 -1
- package/dist/lib/install-plan.js +12 -8
- package/dist/wizard/agent.d.ts +2 -2
- package/dist/wizard/agent.d.ts.map +1 -1
- package/dist/wizard/features.d.ts +28 -0
- package/dist/wizard/features.d.ts.map +1 -0
- package/dist/wizard/run.d.ts.map +1 -1
- package/dist/wizard/wire.d.ts +2 -0
- package/dist/wizard/wire.d.ts.map +1 -1
- package/package.json +2 -2
package/dist/index.js
CHANGED
|
@@ -24695,7 +24695,7 @@ function sleep(ms) {
|
|
|
24695
24695
|
// src/wizard/run.ts
|
|
24696
24696
|
import { existsSync as existsSync9, mkdirSync as mkdirSync2, writeFileSync as writeFileSync8 } from "node:fs";
|
|
24697
24697
|
import { dirname as dirname3 } from "node:path";
|
|
24698
|
-
import * as
|
|
24698
|
+
import * as p4 from "@clack/prompts";
|
|
24699
24699
|
|
|
24700
24700
|
// src/commands/pull.ts
|
|
24701
24701
|
import { writeFileSync as writeFileSync3 } from "node:fs";
|
|
@@ -24839,6 +24839,17 @@ var pullCmd = defineCommand({
|
|
|
24839
24839
|
// src/lib/detect.ts
|
|
24840
24840
|
import { existsSync as existsSync4, readFileSync as readFileSync4 } from "node:fs";
|
|
24841
24841
|
import { dirname as dirname2, join as join5, parse } from "node:path";
|
|
24842
|
+
var LLM_DEP_MAP = {
|
|
24843
|
+
openai: "openai",
|
|
24844
|
+
"@anthropic-ai/sdk": "anthropic",
|
|
24845
|
+
"@langchain/core": "langchain",
|
|
24846
|
+
langchain: "langchain",
|
|
24847
|
+
"@google/generative-ai": "google-genai",
|
|
24848
|
+
ai: "vercel-ai",
|
|
24849
|
+
"cohere-ai": "cohere",
|
|
24850
|
+
"@mistralai/mistralai": "mistral",
|
|
24851
|
+
ollama: "ollama"
|
|
24852
|
+
};
|
|
24842
24853
|
function readPackageJson(dir) {
|
|
24843
24854
|
const p = join5(dir, "package.json");
|
|
24844
24855
|
if (!existsSync4(p))
|
|
@@ -24924,6 +24935,14 @@ function frameworkRuntime(fw) {
|
|
|
24924
24935
|
return "unknown";
|
|
24925
24936
|
}
|
|
24926
24937
|
}
|
|
24938
|
+
function detectLlmFrameworks(pkg) {
|
|
24939
|
+
const found = new Set;
|
|
24940
|
+
for (const [dep, fw] of Object.entries(LLM_DEP_MAP)) {
|
|
24941
|
+
if (hasDep(pkg, dep))
|
|
24942
|
+
found.add(fw);
|
|
24943
|
+
}
|
|
24944
|
+
return [...found];
|
|
24945
|
+
}
|
|
24927
24946
|
function detectProject(dir) {
|
|
24928
24947
|
const pkg = readPackageJson(dir);
|
|
24929
24948
|
const framework = detectFramework(pkg, dir);
|
|
@@ -24933,7 +24952,8 @@ function detectProject(dir) {
|
|
|
24933
24952
|
framework,
|
|
24934
24953
|
runtime: frameworkRuntime(framework),
|
|
24935
24954
|
packageManager: detectPackageManager(dir),
|
|
24936
|
-
packageJson: pkg
|
|
24955
|
+
packageJson: pkg,
|
|
24956
|
+
llmFrameworks: detectLlmFrameworks(pkg)
|
|
24937
24957
|
};
|
|
24938
24958
|
}
|
|
24939
24959
|
|
|
@@ -25267,15 +25287,19 @@ function sdkFor(framework) {
|
|
|
25267
25287
|
return "@gurulu/web";
|
|
25268
25288
|
}
|
|
25269
25289
|
}
|
|
25270
|
-
function
|
|
25290
|
+
function autocaptureLine(jsError, indent) {
|
|
25291
|
+
return jsError ? `
|
|
25292
|
+
${indent}autocapture: { js_error: true }, // hata takibi açık` : "";
|
|
25293
|
+
}
|
|
25294
|
+
function snippetWeb(workspaceKey, jsError = false) {
|
|
25271
25295
|
return `import gurulu from '@gurulu/web';
|
|
25272
25296
|
|
|
25273
25297
|
gurulu.init({
|
|
25274
25298
|
workspaceKey: process.env.NEXT_PUBLIC_GURULU_WORKSPACE ?? '${workspaceKey}',
|
|
25275
|
-
endpoint: process.env.NEXT_PUBLIC_GURULU_ENDPOINT, // optional, defaults to https://ingest.gurulu.io
|
|
25299
|
+
endpoint: process.env.NEXT_PUBLIC_GURULU_ENDPOINT, // optional, defaults to https://ingest.gurulu.io${autocaptureLine(jsError, " ")}
|
|
25276
25300
|
});`;
|
|
25277
25301
|
}
|
|
25278
|
-
function snippetNext(workspaceKey) {
|
|
25302
|
+
function snippetNext(workspaceKey, jsError = false) {
|
|
25279
25303
|
return `// src/app/gurulu-provider.tsx
|
|
25280
25304
|
'use client';
|
|
25281
25305
|
import { useEffect } from 'react';
|
|
@@ -25285,7 +25309,7 @@ export function GuruluProvider({ children }: { children: React.ReactNode }) {
|
|
|
25285
25309
|
useEffect(() => {
|
|
25286
25310
|
gurulu.init({
|
|
25287
25311
|
workspaceKey: process.env.NEXT_PUBLIC_GURULU_WORKSPACE ?? '${workspaceKey}',
|
|
25288
|
-
endpoint: process.env.NEXT_PUBLIC_GURULU_ENDPOINT,
|
|
25312
|
+
endpoint: process.env.NEXT_PUBLIC_GURULU_ENDPOINT,${autocaptureLine(jsError, " ")}
|
|
25289
25313
|
});
|
|
25290
25314
|
}, []);
|
|
25291
25315
|
return <>{children}</>;
|
|
@@ -25364,7 +25388,7 @@ function placementHintFor(framework) {
|
|
|
25364
25388
|
return "Initialize the SDK at your app entry point.";
|
|
25365
25389
|
}
|
|
25366
25390
|
}
|
|
25367
|
-
function initSnippetFor(framework, sdk, workspaceKey) {
|
|
25391
|
+
function initSnippetFor(framework, sdk, workspaceKey, jsError = false) {
|
|
25368
25392
|
if (sdk === "@gurulu/node") {
|
|
25369
25393
|
if (framework === "express")
|
|
25370
25394
|
return snippetExpress();
|
|
@@ -25373,8 +25397,8 @@ function initSnippetFor(framework, sdk, workspaceKey) {
|
|
|
25373
25397
|
return snippetNode();
|
|
25374
25398
|
}
|
|
25375
25399
|
if (framework === "next")
|
|
25376
|
-
return snippetNext(workspaceKey);
|
|
25377
|
-
return snippetWeb(workspaceKey);
|
|
25400
|
+
return snippetNext(workspaceKey, jsError);
|
|
25401
|
+
return snippetWeb(workspaceKey, jsError);
|
|
25378
25402
|
}
|
|
25379
25403
|
function envKeysFor(sdk, framework) {
|
|
25380
25404
|
if (sdk === "@gurulu/node") {
|
|
@@ -25400,7 +25424,7 @@ function buildInstallPlan(detected, ctx = {}) {
|
|
|
25400
25424
|
sdk,
|
|
25401
25425
|
packageManager: detected.packageManager,
|
|
25402
25426
|
installCommand: installCmdFor(detected.packageManager, sdk),
|
|
25403
|
-
initSnippet: initSnippetFor(detected.framework, sdk, workspaceKey),
|
|
25427
|
+
initSnippet: initSnippetFor(detected.framework, sdk, workspaceKey, ctx.autocaptureJsError),
|
|
25404
25428
|
envKeys: envKeysFor(sdk, detected.framework),
|
|
25405
25429
|
placementHint: placementHintFor(detected.framework),
|
|
25406
25430
|
framework: detected.framework
|
|
@@ -25710,8 +25734,77 @@ function gatherContext(opts) {
|
|
|
25710
25734
|
return { files, capped, totalBytes };
|
|
25711
25735
|
}
|
|
25712
25736
|
|
|
25713
|
-
// src/wizard/
|
|
25737
|
+
// src/wizard/features.ts
|
|
25714
25738
|
import * as p2 from "@clack/prompts";
|
|
25739
|
+
function availableFeatures(detected) {
|
|
25740
|
+
const isBrowser = frameworkRuntime(detected.framework) !== "node";
|
|
25741
|
+
const feats = [];
|
|
25742
|
+
feats.push({
|
|
25743
|
+
key: "error",
|
|
25744
|
+
label: "Hata takibi",
|
|
25745
|
+
detail: "Tarayıcı JS hatalarını otomatik yakala (js_error)",
|
|
25746
|
+
enableHint: isBrowser ? "gurulu.init({ ..., autocapture: { js_error: true } })" : "Sunucu hatalarında: gurulu.track('js_error', { message, error_type })",
|
|
25747
|
+
recommended: isBrowser
|
|
25748
|
+
});
|
|
25749
|
+
if (detected.llmFrameworks.length > 0) {
|
|
25750
|
+
feats.push({
|
|
25751
|
+
key: "llm",
|
|
25752
|
+
label: `LLM analizi (${detected.llmFrameworks.join(", ")} tespit edildi)`,
|
|
25753
|
+
detail: "AI çağrı maliyeti/latency/hata/model performansı",
|
|
25754
|
+
enableHint: "import { wrapOpenAI } from '@gurulu/node'; const client = wrapOpenAI(openai);",
|
|
25755
|
+
recommended: true
|
|
25756
|
+
});
|
|
25757
|
+
}
|
|
25758
|
+
if (isBrowser) {
|
|
25759
|
+
feats.push({
|
|
25760
|
+
key: "activation",
|
|
25761
|
+
label: "Activation (popup · tur · A/B test · kişiselleştirme)",
|
|
25762
|
+
detail: "Dashboard'dan yayınla, SDK render etsin (Action Layer)",
|
|
25763
|
+
enableHint: "import { runActivation } from '@gurulu/web/activate'; runActivation(gurulu);",
|
|
25764
|
+
recommended: false
|
|
25765
|
+
});
|
|
25766
|
+
}
|
|
25767
|
+
return feats;
|
|
25768
|
+
}
|
|
25769
|
+
async function runFeatures(client, detected, opts) {
|
|
25770
|
+
const avail = availableFeatures(detected);
|
|
25771
|
+
if (avail.length === 0 || !opts.authed)
|
|
25772
|
+
return { selected: [], registered: false };
|
|
25773
|
+
let selectedKeys;
|
|
25774
|
+
if (opts.yes) {
|
|
25775
|
+
selectedKeys = avail.filter((f3) => f3.recommended).map((f3) => f3.key);
|
|
25776
|
+
} else {
|
|
25777
|
+
const choice = await p2.multiselect({
|
|
25778
|
+
message: "Hangi özellikleri kuralım? (event'ler registry'ye otomatik eklenir)",
|
|
25779
|
+
options: avail.map((f3) => ({ value: f3.key, label: f3.label, hint: f3.detail })),
|
|
25780
|
+
initialValues: avail.filter((f3) => f3.recommended).map((f3) => f3.key),
|
|
25781
|
+
required: false
|
|
25782
|
+
});
|
|
25783
|
+
if (p2.isCancel(choice))
|
|
25784
|
+
return { selected: [], registered: false };
|
|
25785
|
+
selectedKeys = choice;
|
|
25786
|
+
}
|
|
25787
|
+
if (selectedKeys.length === 0)
|
|
25788
|
+
return { selected: [], registered: false };
|
|
25789
|
+
const selected = avail.filter((f3) => selectedKeys.includes(f3.key));
|
|
25790
|
+
const s2 = p2.spinner();
|
|
25791
|
+
s2.start("Özellikler registry'ye kuruluyor…");
|
|
25792
|
+
try {
|
|
25793
|
+
await client.post("/features/register", { features: selectedKeys });
|
|
25794
|
+
s2.stop(c3.neon(`✓ ${selected.length} özellik kuruldu`));
|
|
25795
|
+
} catch {
|
|
25796
|
+
s2.stop("Özellik kurulumu atlandı (gateway hatası) — sonra: gurulu doctor", 1);
|
|
25797
|
+
return { selected, registered: false };
|
|
25798
|
+
}
|
|
25799
|
+
p2.note(selected.map((f3) => `${c3.bold(f3.label)}
|
|
25800
|
+
${c3.dim(f3.enableHint)}`).join(`
|
|
25801
|
+
|
|
25802
|
+
`), "Etkinleştirme");
|
|
25803
|
+
return { selected, registered: true };
|
|
25804
|
+
}
|
|
25805
|
+
|
|
25806
|
+
// src/wizard/plan.ts
|
|
25807
|
+
import * as p3 from "@clack/prompts";
|
|
25715
25808
|
async function fetchPlan(client, context, input) {
|
|
25716
25809
|
if (context.files.length === 0)
|
|
25717
25810
|
return null;
|
|
@@ -25756,12 +25849,12 @@ function formatPlan(plan) {
|
|
|
25756
25849
|
`).trimEnd();
|
|
25757
25850
|
}
|
|
25758
25851
|
async function renderPlan(plan) {
|
|
25759
|
-
|
|
25852
|
+
p3.note(formatPlan(plan), plan.summary);
|
|
25760
25853
|
const newCount = plan.events.filter((e2) => !e2.existing).length;
|
|
25761
|
-
const ok = await
|
|
25854
|
+
const ok = await p3.confirm({
|
|
25762
25855
|
message: `${plan.events.length} event (${newCount} yeni) — bu planı uygula?`
|
|
25763
25856
|
});
|
|
25764
|
-
if (
|
|
25857
|
+
if (p3.isCancel(ok) || !ok)
|
|
25765
25858
|
return null;
|
|
25766
25859
|
return plan.events;
|
|
25767
25860
|
}
|
|
@@ -25776,8 +25869,8 @@ import { promisify } from "node:util";
|
|
|
25776
25869
|
|
|
25777
25870
|
// src/wizard/guard.ts
|
|
25778
25871
|
import { basename, isAbsolute, relative as relative2, resolve } from "node:path";
|
|
25779
|
-
function resolveInCwd(
|
|
25780
|
-
return isAbsolute(
|
|
25872
|
+
function resolveInCwd(p4, cwd) {
|
|
25873
|
+
return isAbsolute(p4) ? p4 : resolve(cwd, p4);
|
|
25781
25874
|
}
|
|
25782
25875
|
function isAdditiveEdit(find, replace) {
|
|
25783
25876
|
if (find.length === 0)
|
|
@@ -25854,8 +25947,8 @@ function isAllowedBash(cmd) {
|
|
|
25854
25947
|
}
|
|
25855
25948
|
return { ok: false, reason: `allowlist dışı binary: ${bin}` };
|
|
25856
25949
|
}
|
|
25857
|
-
function isAllowedPath(
|
|
25858
|
-
const abs = isAbsolute(
|
|
25950
|
+
function isAllowedPath(p4, cwd) {
|
|
25951
|
+
const abs = isAbsolute(p4) ? p4 : resolve(cwd, p4);
|
|
25859
25952
|
const rel = relative2(cwd, abs);
|
|
25860
25953
|
if (rel === "" || rel.startsWith("..") || isAbsolute(rel)) {
|
|
25861
25954
|
return { ok: false, reason: "cwd dışı yol" };
|
|
@@ -25966,11 +26059,14 @@ ${stderr}`.trim().slice(0, MAX_OBS);
|
|
|
25966
26059
|
}
|
|
25967
26060
|
function buildWireSystemPrompt() {
|
|
25968
26061
|
return [
|
|
25969
|
-
"You are Gurulu's capture wire agent. Add the approved analytics events to the user's code
|
|
26062
|
+
"You are Gurulu's capture wire agent. Add the approved analytics events to the user's code,",
|
|
26063
|
+
"plus any additional wiring tasks given (e.g. wrapping an LLM client, mounting a provider).",
|
|
25970
26064
|
"Tools (one JSON action per turn): read{path} | edit{path,find,replace} | bash{cmd} | done{summary}.",
|
|
25971
26065
|
"HARD RULES:",
|
|
25972
26066
|
"- ADDITIVE-ONLY: every edit `replace` MUST contain `find` verbatim — you may only ADD code,",
|
|
25973
|
-
|
|
26067
|
+
' never delete or rewrite existing code. Edits violating this are rejected. To "wrap" an existing',
|
|
26068
|
+
" expression, keep the original verbatim inside the new wrapper (e.g. find `new OpenAI(cfg)` →",
|
|
26069
|
+
" replace `wrapOpenAI(new OpenAI(cfg))`), and add the import additively.",
|
|
25974
26070
|
"- `find` must be an EXACT, UNIQUE snippet copied from a file you have read (read before edit).",
|
|
25975
26071
|
"- Use the exact provided event_key (snake_case). Wire gurulu.track(...) at the right place and",
|
|
25976
26072
|
" gurulu.identify(...) at the auth point if given.",
|
|
@@ -25978,21 +26074,23 @@ function buildWireSystemPrompt() {
|
|
|
25978
26074
|
" `pnpm run lint`) or a checker binary (`tsc --noEmit`, `biome check`, `eslint .`). Installing or",
|
|
25979
26075
|
" fetching packages (`npm install`, `npx`, `bunx`, `pnpm dlx`, `add`, `exec`) is REJECTED by the guard.",
|
|
25980
26076
|
"- After wiring, run the project typecheck/build to verify; if it breaks, fix additively.",
|
|
25981
|
-
"- When all events are
|
|
26077
|
+
"- When all events AND additional tasks are done and verify passes, emit done{summary}. Keep edits minimal."
|
|
25982
26078
|
].join(`
|
|
25983
26079
|
`);
|
|
25984
26080
|
}
|
|
25985
|
-
function buildWireUserPrompt(events, identifyHint, files) {
|
|
25986
|
-
const
|
|
26081
|
+
function buildWireUserPrompt(events, identifyHint, files, extraTasks = []) {
|
|
26082
|
+
const sections = [];
|
|
26083
|
+
if (events.length > 0) {
|
|
26084
|
+
const evLines = events.map((e2) => `- ${e2.event_key} (${e2.event_type})${e2.capture_hint ? ` @ ${e2.capture_hint}` : ""}: ${e2.reason}`).join(`
|
|
25987
26085
|
`);
|
|
25988
|
-
|
|
25989
|
-
|
|
25990
|
-
|
|
25991
|
-
|
|
25992
|
-
|
|
25993
|
-
|
|
25994
|
-
|
|
25995
|
-
|
|
26086
|
+
sections.push("Wire these events into the codebase:", evLines, identifyHint ? `identify() at: ${identifyHint}` : "No identify hint.");
|
|
26087
|
+
}
|
|
26088
|
+
if (extraTasks.length > 0) {
|
|
26089
|
+
sections.push("Additional wiring tasks:", extraTasks.map((t2, i2) => `${i2 + 1}. ${t2}`).join(`
|
|
26090
|
+
`));
|
|
26091
|
+
}
|
|
26092
|
+
sections.push("", `Project files (read the relevant ones): ${files.join(", ")}`, "Start by reading the files you need, then edit additively.");
|
|
26093
|
+
return sections.join(`
|
|
25996
26094
|
`);
|
|
25997
26095
|
}
|
|
25998
26096
|
|
|
@@ -26001,7 +26099,10 @@ var MAX_STEPS = 25;
|
|
|
26001
26099
|
async function runWireAgent(client, input, snapshots) {
|
|
26002
26100
|
const messages = [
|
|
26003
26101
|
{ role: "system", content: buildWireSystemPrompt() },
|
|
26004
|
-
{
|
|
26102
|
+
{
|
|
26103
|
+
role: "user",
|
|
26104
|
+
content: buildWireUserPrompt(input.events, input.identifyHint, input.files, input.extraTasks ?? [])
|
|
26105
|
+
}
|
|
26005
26106
|
];
|
|
26006
26107
|
const edits = [];
|
|
26007
26108
|
const changed = new Set;
|
|
@@ -26119,16 +26220,16 @@ var FRAMEWORKS = [
|
|
|
26119
26220
|
"node-server"
|
|
26120
26221
|
];
|
|
26121
26222
|
function bail() {
|
|
26122
|
-
|
|
26223
|
+
p4.cancel("İptal edildi.");
|
|
26123
26224
|
process.exit(0);
|
|
26124
26225
|
}
|
|
26125
26226
|
async function runWizard(opts) {
|
|
26126
26227
|
if (process.stdout.isTTY)
|
|
26127
26228
|
process.stdout.write(banner());
|
|
26128
|
-
|
|
26229
|
+
p4.intro(c3.dim("otonom kurulum başlıyor — auth → workspace → install → wire"));
|
|
26129
26230
|
const TOTAL = 6;
|
|
26130
26231
|
const phase = (n2, label) => {
|
|
26131
|
-
|
|
26232
|
+
p4.log.step(`${c3.dim(`[${n2}/${TOTAL}]`)} ${c3.bold(label)}`);
|
|
26132
26233
|
};
|
|
26133
26234
|
phase(1, "Kimlik doğrulama");
|
|
26134
26235
|
const auth = await ensureAuth({
|
|
@@ -26145,25 +26246,23 @@ async function runWizard(opts) {
|
|
|
26145
26246
|
if (opts.framework && FRAMEWORKS.includes(opts.framework)) {
|
|
26146
26247
|
framework = opts.framework;
|
|
26147
26248
|
} else if (!opts.yes) {
|
|
26148
|
-
const ok = await
|
|
26249
|
+
const ok = await p4.confirm({
|
|
26149
26250
|
message: `Saptanan: ${detected.framework} (${detected.packageManager}). Doğru mu?`
|
|
26150
26251
|
});
|
|
26151
|
-
if (
|
|
26252
|
+
if (p4.isCancel(ok))
|
|
26152
26253
|
bail();
|
|
26153
26254
|
if (!ok) {
|
|
26154
|
-
const choice = await
|
|
26255
|
+
const choice = await p4.select({
|
|
26155
26256
|
message: "Framework seç",
|
|
26156
26257
|
options: FRAMEWORKS.map((f3) => ({ value: f3, label: f3 })),
|
|
26157
26258
|
initialValue: detected.framework
|
|
26158
26259
|
});
|
|
26159
|
-
if (
|
|
26260
|
+
if (p4.isCancel(choice))
|
|
26160
26261
|
bail();
|
|
26161
26262
|
framework = choice;
|
|
26162
26263
|
}
|
|
26163
26264
|
}
|
|
26164
26265
|
const project = { ...detected, framework };
|
|
26165
|
-
const plan = buildInstallPlan(project, { writeKey, workspaceId });
|
|
26166
|
-
const isNode = plan.sdk === "@gurulu/node";
|
|
26167
26266
|
const authed = Boolean(auth.apiKey);
|
|
26168
26267
|
let approvedEvents = [];
|
|
26169
26268
|
let identifyHint = null;
|
|
@@ -26176,17 +26275,28 @@ async function runWizard(opts) {
|
|
|
26176
26275
|
if (approved)
|
|
26177
26276
|
approvedEvents = approved;
|
|
26178
26277
|
} else {
|
|
26179
|
-
|
|
26278
|
+
p4.log.info("AI planı yok — autocapture + sektör paketi (deterministik floor).");
|
|
26180
26279
|
}
|
|
26181
26280
|
}
|
|
26281
|
+
let featuresResult = { selected: [], registered: false };
|
|
26282
|
+
if (authed) {
|
|
26283
|
+
featuresResult = await runFeatures(client, project, { yes: opts.yes, authed });
|
|
26284
|
+
}
|
|
26285
|
+
const errorSelected = featuresResult.selected.some((f3) => f3.key === "error");
|
|
26286
|
+
const plan = buildInstallPlan(project, {
|
|
26287
|
+
writeKey,
|
|
26288
|
+
workspaceId,
|
|
26289
|
+
autocaptureJsError: errorSelected
|
|
26290
|
+
});
|
|
26291
|
+
const isNode = plan.sdk === "@gurulu/node";
|
|
26182
26292
|
phase(4, "SDK kurulumu");
|
|
26183
26293
|
let installed = false;
|
|
26184
26294
|
if (opts.noInstall) {
|
|
26185
|
-
|
|
26295
|
+
p4.log.info(`SDK kurulumu atlandı — elle: ${plan.installCommand}`);
|
|
26186
26296
|
} else if (!detected.hasPackageJson) {
|
|
26187
|
-
|
|
26297
|
+
p4.log.warn("package.json yok — script tag ile kur: https://cdn.gurulu.io/t.js");
|
|
26188
26298
|
} else {
|
|
26189
|
-
const s2 =
|
|
26299
|
+
const s2 = p4.spinner();
|
|
26190
26300
|
s2.start(`${plan.sdk} kuruluyor (${plan.installCommand})…`);
|
|
26191
26301
|
const res = await execInstall(plan, { cwd: opts.cwd });
|
|
26192
26302
|
if (res.ok) {
|
|
@@ -26204,6 +26314,14 @@ async function runWizard(opts) {
|
|
|
26204
26314
|
snippet: plan.initSnippet,
|
|
26205
26315
|
placementHint: plan.placementHint
|
|
26206
26316
|
});
|
|
26317
|
+
const extraTasks = [];
|
|
26318
|
+
const llmSelected = featuresResult.selected.some((f3) => f3.key === "llm");
|
|
26319
|
+
if (llmSelected && detected.llmFrameworks.length > 0) {
|
|
26320
|
+
extraTasks.push(`Wrap the project's LLM client(s) [${detected.llmFrameworks.join(", ")}] with @gurulu/node so ` + "every call is auto-captured. Find where the OpenAI/Anthropic client is instantiated and wrap " + "it in place additively: `import { wrapOpenAI } from '@gurulu/node';` then " + "`const client = wrapOpenAI(new OpenAI({...}))` (use wrapAnthropic for Anthropic SDK). " + "Keep the original instantiation verbatim inside the wrapper.");
|
|
26321
|
+
}
|
|
26322
|
+
if (inj.strategy === "create-module" && inj.file && (project.framework === "next" || project.framework === "nuxt")) {
|
|
26323
|
+
extraTasks.push(project.framework === "next" ? `Mount the Gurulu provider created at \`${inj.file}\` into the app root. Edit app/layout.tsx ` + "(App Router) or pages/_app.tsx (Pages Router) to import { GuruluProvider } from that module " + "and wrap the children additively: `<GuruluProvider>{children}</GuruluProvider>`." : `Register the Nuxt plugin created at \`${inj.file}\` so it loads on the client. If it is under ` + "plugins/ Nuxt auto-registers it; otherwise add it to the plugins array in nuxt.config.ts additively.");
|
|
26324
|
+
}
|
|
26207
26325
|
const envFile = isNode ? ".env" : ".env.local";
|
|
26208
26326
|
const vars = [];
|
|
26209
26327
|
for (const k2 of plan.envKeys) {
|
|
@@ -26213,7 +26331,7 @@ async function runWizard(opts) {
|
|
|
26213
26331
|
if (auth.apiKey.startsWith("sk_")) {
|
|
26214
26332
|
vars.push({ key: k2.key, value: auth.apiKey });
|
|
26215
26333
|
} else {
|
|
26216
|
-
|
|
26334
|
+
p4.log.warn("GURULU_SECRET_KEY atlandı (login token workspace sk_ anahtarı değil). Dashboard → Settings → API Keys'ten server key oluşturup .env'e elle ekle.");
|
|
26217
26335
|
}
|
|
26218
26336
|
}
|
|
26219
26337
|
}
|
|
@@ -26233,38 +26351,45 @@ async function runWizard(opts) {
|
|
|
26233
26351
|
}
|
|
26234
26352
|
let registeredCount = 0;
|
|
26235
26353
|
if (approvedEvents.length > 0) {
|
|
26236
|
-
const s2 =
|
|
26354
|
+
const s2 = p4.spinner();
|
|
26237
26355
|
s2.start("Yeni eventler registry kuyruguna oneriliyor…");
|
|
26238
26356
|
const res = await registerNewEvents(client, approvedEvents);
|
|
26239
26357
|
registeredCount = res.registered.length;
|
|
26240
26358
|
s2.stop(`${res.registered.length} yeni event verification queue'ya eklendi${res.failed.length > 0 ? ` (${res.failed.length} başarısız)` : ""}`);
|
|
26241
26359
|
}
|
|
26242
26360
|
let wiredCount = 0;
|
|
26243
|
-
|
|
26361
|
+
const hasWireWork = approvedEvents.length > 0 || extraTasks.length > 0;
|
|
26362
|
+
if (hasWireWork) {
|
|
26244
26363
|
if (opts.noAi) {
|
|
26245
|
-
|
|
26364
|
+
if (approvedEvents.length > 0) {
|
|
26365
|
+
p4.log.message(`Capture — şu çağrıları ekle:
|
|
26246
26366
|
${captureGuide(approvedEvents, identifyHint, isNode)}`);
|
|
26367
|
+
}
|
|
26247
26368
|
} else {
|
|
26248
26369
|
const wireFiles = gatherContext({ cwd: opts.cwd }).files.map((f3) => f3.path);
|
|
26249
26370
|
const snapshots = new Map;
|
|
26250
|
-
const s2 =
|
|
26371
|
+
const s2 = p4.spinner();
|
|
26251
26372
|
s2.start("AI capture wiring (kod düzenleniyor)…");
|
|
26252
|
-
const outcome = await runWireAgent(client, { cwd: opts.cwd, events: approvedEvents, identifyHint, files: wireFiles }, snapshots);
|
|
26373
|
+
const outcome = await runWireAgent(client, { cwd: opts.cwd, events: approvedEvents, identifyHint, files: wireFiles, extraTasks }, snapshots);
|
|
26253
26374
|
s2.stop(`wire: ${outcome.changedFiles.length} dosya / ${outcome.steps} adım (${outcome.stoppedReason})`);
|
|
26254
26375
|
if (outcome.changedFiles.length > 0) {
|
|
26255
|
-
|
|
26256
|
-
const keep = opts.yes ? true : await
|
|
26376
|
+
p4.log.message(formatWireDiff(opts.cwd, snapshots));
|
|
26377
|
+
const keep = opts.yes ? true : await p4.confirm({
|
|
26257
26378
|
message: `${outcome.changedFiles.length} dosyadaki wire değişikliklerini tut?`
|
|
26258
26379
|
});
|
|
26259
|
-
if (
|
|
26380
|
+
if (p4.isCancel(keep) || !keep) {
|
|
26260
26381
|
restoreSnapshots(opts.cwd, snapshots);
|
|
26261
|
-
|
|
26262
|
-
|
|
26382
|
+
if (approvedEvents.length > 0) {
|
|
26383
|
+
p4.log.info("Wire geri alındı — capture snippet rehberi:");
|
|
26384
|
+
p4.log.message(captureGuide(approvedEvents, identifyHint, isNode));
|
|
26385
|
+
} else {
|
|
26386
|
+
p4.log.info("Wire geri alındı — etkinleştirme snippet'leri yukarıda.");
|
|
26387
|
+
}
|
|
26263
26388
|
} else {
|
|
26264
26389
|
wiredCount = outcome.changedFiles.length;
|
|
26265
26390
|
}
|
|
26266
|
-
} else {
|
|
26267
|
-
|
|
26391
|
+
} else if (approvedEvents.length > 0) {
|
|
26392
|
+
p4.log.message(`Capture (AI gömemedi → snippet-göster) — şu çağrıları ekle:
|
|
26268
26393
|
${captureGuide(approvedEvents, identifyHint, isNode)}`);
|
|
26269
26394
|
}
|
|
26270
26395
|
}
|
|
@@ -26280,18 +26405,21 @@ ${captureGuide(approvedEvents, identifyHint, isNode)}`);
|
|
|
26280
26405
|
}
|
|
26281
26406
|
if (approvedEvents.length > 0) {
|
|
26282
26407
|
lines.push(`✓ ${approvedEvents.length} event planlandı, ${registeredCount} yeni registry kuyruğunda`);
|
|
26283
|
-
|
|
26284
|
-
|
|
26408
|
+
}
|
|
26409
|
+
if (wiredCount > 0)
|
|
26410
|
+
lines.push(`✓ ${wiredCount} dosya AI ile wire edildi (capture/LLM/provider)`);
|
|
26411
|
+
if (featuresResult.registered && featuresResult.selected.length > 0) {
|
|
26412
|
+
lines.push(`✓ özellikler: ${featuresResult.selected.map((f3) => f3.key).join(", ")} kuruldu`);
|
|
26285
26413
|
}
|
|
26286
26414
|
lines.push(`✓ .gurulu/config.json${pulled ? " + registry pull" : ""}`);
|
|
26287
|
-
|
|
26415
|
+
p4.note(lines.join(`
|
|
26288
26416
|
`), c3.neon("✦ Değişiklikler"));
|
|
26289
26417
|
if (inj.strategy !== "prepend-entry" || inj.reason === "no-entry") {
|
|
26290
|
-
|
|
26418
|
+
p4.log.info(`Wire: ${inj.wireHint ?? plan.placementHint}`);
|
|
26291
26419
|
if (inj.strategy === "manual")
|
|
26292
|
-
|
|
26420
|
+
p4.log.message(plan.initSnippet);
|
|
26293
26421
|
}
|
|
26294
|
-
|
|
26422
|
+
p4.outro(`${c3.neon("\uD83E\uDD89 Hazır!")} ${c3.dim("Doğrula:")} ${c3.bold("gurulu doctor")} ${c3.dim("·")} ${c3.dim("Dashboard:")} ${c3.cyan("https://dashboard.gurulu.io/app?onboard=done")}`);
|
|
26295
26423
|
}
|
|
26296
26424
|
async function resolveWorkspace(client, authWorkspaceId, opts) {
|
|
26297
26425
|
if (opts.writeKey) {
|
|
@@ -26314,7 +26442,7 @@ async function resolveWorkspace(client, authWorkspaceId, opts) {
|
|
|
26314
26442
|
selected = CREATE;
|
|
26315
26443
|
} else {
|
|
26316
26444
|
const initial = list.find((w2) => w2.workspace_id === authWorkspaceId)?.workspace_id ?? list[0]?.workspace_id ?? CREATE;
|
|
26317
|
-
const choice = await
|
|
26445
|
+
const choice = await p4.select({
|
|
26318
26446
|
message: "Workspace seç",
|
|
26319
26447
|
options: [
|
|
26320
26448
|
...list.map((w2) => ({ value: w2.workspace_id, label: w2.name, hint: w2.slug })),
|
|
@@ -26322,26 +26450,26 @@ async function resolveWorkspace(client, authWorkspaceId, opts) {
|
|
|
26322
26450
|
],
|
|
26323
26451
|
initialValue: initial
|
|
26324
26452
|
});
|
|
26325
|
-
if (
|
|
26453
|
+
if (p4.isCancel(choice))
|
|
26326
26454
|
bail();
|
|
26327
26455
|
selected = choice;
|
|
26328
26456
|
}
|
|
26329
26457
|
if (selected === CREATE) {
|
|
26330
|
-
const name = await
|
|
26458
|
+
const name = await p4.text({
|
|
26331
26459
|
message: "Workspace adı",
|
|
26332
26460
|
placeholder: "My App",
|
|
26333
26461
|
validate: (v2) => v2.trim() ? undefined : "gerekli"
|
|
26334
26462
|
});
|
|
26335
|
-
if (
|
|
26463
|
+
if (p4.isCancel(name))
|
|
26336
26464
|
bail();
|
|
26337
|
-
const domain = await
|
|
26465
|
+
const domain = await p4.text({
|
|
26338
26466
|
message: "Domain",
|
|
26339
26467
|
placeholder: "example.com",
|
|
26340
26468
|
validate: (v2) => v2.trim().length >= 3 ? undefined : "geçerli domain gerekli"
|
|
26341
26469
|
});
|
|
26342
|
-
if (
|
|
26470
|
+
if (p4.isCancel(domain))
|
|
26343
26471
|
bail();
|
|
26344
|
-
const s2 =
|
|
26472
|
+
const s2 = p4.spinner();
|
|
26345
26473
|
s2.start("Workspace oluşturuluyor…");
|
|
26346
26474
|
const created = await client.createWorkspace({
|
|
26347
26475
|
name: String(name).trim(),
|
|
@@ -26666,7 +26794,7 @@ import { execFile as execFile2 } from "node:child_process";
|
|
|
26666
26794
|
import { existsSync as existsSync11, readFileSync as readFileSync11, rmSync, writeFileSync as writeFileSync10 } from "node:fs";
|
|
26667
26795
|
import { join as join10 } from "node:path";
|
|
26668
26796
|
import { promisify as promisify2 } from "node:util";
|
|
26669
|
-
import * as
|
|
26797
|
+
import * as p5 from "@clack/prompts";
|
|
26670
26798
|
var pexec2 = promisify2(execFile2);
|
|
26671
26799
|
var ENV_FILES = [".env.local", ".env"];
|
|
26672
26800
|
var GURULU_PREFIXES = ["GURULU_", "NEXT_PUBLIC_GURULU", "VITE_GURULU"];
|
|
@@ -26693,23 +26821,23 @@ var uninstallCmd = defineCommand({
|
|
|
26693
26821
|
const config = readProjectConfig(cwd);
|
|
26694
26822
|
const detected = detectProject(cwd);
|
|
26695
26823
|
const pkg = config?.sdk_preference === "node" ? "@gurulu/node" : "@gurulu/web";
|
|
26696
|
-
|
|
26697
|
-
|
|
26824
|
+
p5.intro("Gurulu uninstall");
|
|
26825
|
+
p5.note([
|
|
26698
26826
|
`• SDK kaldır: ${pkg} (${detected.packageManager})`,
|
|
26699
26827
|
`• env temizle: ${ENV_FILES.join(", ")} (GURULU_* anahtarları)`,
|
|
26700
26828
|
"• .gurulu/ dizinini sil"
|
|
26701
26829
|
].join(`
|
|
26702
26830
|
`), "Şunlar yapılacak");
|
|
26703
26831
|
if (!args.yes) {
|
|
26704
|
-
const ok = await
|
|
26705
|
-
if (
|
|
26706
|
-
|
|
26832
|
+
const ok = await p5.confirm({ message: "Devam edilsin mi?" });
|
|
26833
|
+
if (p5.isCancel(ok) || !ok) {
|
|
26834
|
+
p5.cancel("İptal.");
|
|
26707
26835
|
process.exit(0);
|
|
26708
26836
|
}
|
|
26709
26837
|
}
|
|
26710
26838
|
if (!args["no-deps"] && detected.hasPackageJson) {
|
|
26711
26839
|
const { bin, args: a2 } = removeCmd(detected.packageManager, pkg);
|
|
26712
|
-
const s2 =
|
|
26840
|
+
const s2 = p5.spinner();
|
|
26713
26841
|
s2.start(`${pkg} kaldırılıyor…`);
|
|
26714
26842
|
try {
|
|
26715
26843
|
await pexec2(bin, a2, { cwd, timeout: 120000 });
|
|
@@ -26732,7 +26860,7 @@ var uninstallCmd = defineCommand({
|
|
|
26732
26860
|
const guruluDir = join10(cwd, ".gurulu");
|
|
26733
26861
|
if (existsSync11(guruluDir))
|
|
26734
26862
|
rmSync(guruluDir, { recursive: true, force: true });
|
|
26735
|
-
|
|
26863
|
+
p5.outro(`Kaldırıldı. env: ${cleaned.join(", ") || "değişiklik yok"} · .gurulu silindi. (Koddaki init/track çağrılarını elle çıkar.)`);
|
|
26736
26864
|
}
|
|
26737
26865
|
});
|
|
26738
26866
|
|
package/dist/lib/detect.d.ts
CHANGED
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
export type Framework = 'next' | 'react' | 'vue' | 'nuxt' | 'svelte' | 'astro' | 'vite' | 'express' | 'fastify' | 'hono' | 'koa' | 'node-server' | 'unknown';
|
|
2
2
|
export type Runtime = 'browser' | 'node' | 'unknown';
|
|
3
|
+
export type LlmFramework = 'openai' | 'anthropic' | 'langchain' | 'google-genai' | 'vercel-ai' | 'cohere' | 'mistral' | 'ollama';
|
|
3
4
|
export type PackageManager = 'bun' | 'pnpm' | 'yarn' | 'npm';
|
|
4
5
|
export interface DetectedProject {
|
|
5
6
|
dir: string;
|
|
@@ -8,6 +9,8 @@ export interface DetectedProject {
|
|
|
8
9
|
runtime: Runtime;
|
|
9
10
|
packageManager: PackageManager;
|
|
10
11
|
packageJson: PackageJsonShape | null;
|
|
12
|
+
/** M46 (K31) — tespit edilen LLM/AI bağımlılıkları (boş = yok). */
|
|
13
|
+
llmFrameworks: LlmFramework[];
|
|
11
14
|
}
|
|
12
15
|
interface PackageJsonShape {
|
|
13
16
|
name?: string;
|
|
@@ -22,6 +25,8 @@ interface PackageJsonShape {
|
|
|
22
25
|
export declare function detectPackageManager(dir: string): PackageManager;
|
|
23
26
|
export declare function detectFramework(pkg: PackageJsonShape | null, dir: string): Framework;
|
|
24
27
|
export declare function frameworkRuntime(fw: Framework): Runtime;
|
|
28
|
+
/** package.json dependencies içinde LLM/AI SDK'larını tespit eder (dedup). */
|
|
29
|
+
export declare function detectLlmFrameworks(pkg: PackageJsonShape | null): LlmFramework[];
|
|
25
30
|
export declare function detectProject(dir: string): DetectedProject;
|
|
26
31
|
export {};
|
|
27
32
|
//# sourceMappingURL=detect.d.ts.map
|
package/dist/lib/detect.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"detect.d.ts","sourceRoot":"","sources":["../../src/lib/detect.ts"],"names":[],"mappings":"AAMA,MAAM,MAAM,SAAS,GACjB,MAAM,GACN,OAAO,GACP,KAAK,GACL,MAAM,GACN,QAAQ,GACR,OAAO,GACP,MAAM,GACN,SAAS,GACT,SAAS,GACT,MAAM,GACN,KAAK,GACL,aAAa,GACb,SAAS,CAAC;AAEd,MAAM,MAAM,OAAO,GAAG,SAAS,GAAG,MAAM,GAAG,SAAS,CAAC;
|
|
1
|
+
{"version":3,"file":"detect.d.ts","sourceRoot":"","sources":["../../src/lib/detect.ts"],"names":[],"mappings":"AAMA,MAAM,MAAM,SAAS,GACjB,MAAM,GACN,OAAO,GACP,KAAK,GACL,MAAM,GACN,QAAQ,GACR,OAAO,GACP,MAAM,GACN,SAAS,GACT,SAAS,GACT,MAAM,GACN,KAAK,GACL,aAAa,GACb,SAAS,CAAC;AAEd,MAAM,MAAM,OAAO,GAAG,SAAS,GAAG,MAAM,GAAG,SAAS,CAAC;AAGrD,MAAM,MAAM,YAAY,GACpB,QAAQ,GACR,WAAW,GACX,WAAW,GACX,cAAc,GACd,WAAW,GACX,QAAQ,GACR,SAAS,GACT,QAAQ,CAAC;AAeb,MAAM,MAAM,cAAc,GAAG,KAAK,GAAG,MAAM,GAAG,MAAM,GAAG,KAAK,CAAC;AAE7D,MAAM,WAAW,eAAe;IAC9B,GAAG,EAAE,MAAM,CAAC;IACZ,cAAc,EAAE,OAAO,CAAC;IACxB,SAAS,EAAE,SAAS,CAAC;IACrB,OAAO,EAAE,OAAO,CAAC;IACjB,cAAc,EAAE,cAAc,CAAC;IAC/B,WAAW,EAAE,gBAAgB,GAAG,IAAI,CAAC;IACrC,mEAAmE;IACnE,aAAa,EAAE,YAAY,EAAE,CAAC;CAC/B;AAED,UAAU,gBAAgB;IACxB,IAAI,CAAC,EAAE,MAAM,CAAC;IACd,IAAI,CAAC,EAAE,QAAQ,GAAG,UAAU,CAAC;IAC7B,YAAY,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;IACtC,eAAe,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;CAC1C;AAiBD;;;GAGG;AACH,wBAAgB,oBAAoB,CAAC,GAAG,EAAE,MAAM,GAAG,cAAc,CAchE;AAED,wBAAgB,eAAe,CAAC,GAAG,EAAE,gBAAgB,GAAG,IAAI,EAAE,GAAG,EAAE,MAAM,GAAG,SAAS,CAmBpF;AAED,wBAAgB,gBAAgB,CAAC,EAAE,EAAE,SAAS,GAAG,OAAO,CAmBvD;AAED,8EAA8E;AAC9E,wBAAgB,mBAAmB,CAAC,GAAG,EAAE,gBAAgB,GAAG,IAAI,GAAG,YAAY,EAAE,CAMhF;AAED,wBAAgB,aAAa,CAAC,GAAG,EAAE,MAAM,GAAG,eAAe,CAY1D"}
|
package/dist/lib/detect.js
CHANGED
|
@@ -1,6 +1,17 @@
|
|
|
1
1
|
// src/lib/detect.ts
|
|
2
2
|
import { existsSync, readFileSync } from "node:fs";
|
|
3
3
|
import { dirname, join, parse } from "node:path";
|
|
4
|
+
var LLM_DEP_MAP = {
|
|
5
|
+
openai: "openai",
|
|
6
|
+
"@anthropic-ai/sdk": "anthropic",
|
|
7
|
+
"@langchain/core": "langchain",
|
|
8
|
+
langchain: "langchain",
|
|
9
|
+
"@google/generative-ai": "google-genai",
|
|
10
|
+
ai: "vercel-ai",
|
|
11
|
+
"cohere-ai": "cohere",
|
|
12
|
+
"@mistralai/mistralai": "mistral",
|
|
13
|
+
ollama: "ollama"
|
|
14
|
+
};
|
|
4
15
|
function readPackageJson(dir) {
|
|
5
16
|
const p = join(dir, "package.json");
|
|
6
17
|
if (!existsSync(p))
|
|
@@ -86,6 +97,14 @@ function frameworkRuntime(fw) {
|
|
|
86
97
|
return "unknown";
|
|
87
98
|
}
|
|
88
99
|
}
|
|
100
|
+
function detectLlmFrameworks(pkg) {
|
|
101
|
+
const found = new Set;
|
|
102
|
+
for (const [dep, fw] of Object.entries(LLM_DEP_MAP)) {
|
|
103
|
+
if (hasDep(pkg, dep))
|
|
104
|
+
found.add(fw);
|
|
105
|
+
}
|
|
106
|
+
return [...found];
|
|
107
|
+
}
|
|
89
108
|
function detectProject(dir) {
|
|
90
109
|
const pkg = readPackageJson(dir);
|
|
91
110
|
const framework = detectFramework(pkg, dir);
|
|
@@ -95,12 +114,14 @@ function detectProject(dir) {
|
|
|
95
114
|
framework,
|
|
96
115
|
runtime: frameworkRuntime(framework),
|
|
97
116
|
packageManager: detectPackageManager(dir),
|
|
98
|
-
packageJson: pkg
|
|
117
|
+
packageJson: pkg,
|
|
118
|
+
llmFrameworks: detectLlmFrameworks(pkg)
|
|
99
119
|
};
|
|
100
120
|
}
|
|
101
121
|
export {
|
|
102
122
|
frameworkRuntime,
|
|
103
123
|
detectProject,
|
|
104
124
|
detectPackageManager,
|
|
125
|
+
detectLlmFrameworks,
|
|
105
126
|
detectFramework
|
|
106
127
|
};
|
|
@@ -7,6 +7,12 @@ export interface InstallPlanContext {
|
|
|
7
7
|
workspaceId?: string;
|
|
8
8
|
/** Server-side projeler için API key (sk_xxx). */
|
|
9
9
|
apiKey?: string;
|
|
10
|
+
/**
|
|
11
|
+
* "Hata takibi" özelliği seçildiyse browser init snippet'ine
|
|
12
|
+
* `autocapture: { js_error: true }` eklenir (default autocapture kapalı —
|
|
13
|
+
* registry'ye event kurmak yetmez, SDK'da da açılmalı). Sadece browser SDK.
|
|
14
|
+
*/
|
|
15
|
+
autocaptureJsError?: boolean;
|
|
10
16
|
}
|
|
11
17
|
export interface InstallPlan {
|
|
12
18
|
sdk: SdkPackage;
|