@dosu/cli 0.53.1 → 0.54.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/bin/dosu.js +1295 -1191
- package/package.json +1 -1
package/bin/dosu.js
CHANGED
|
@@ -5018,7 +5018,7 @@ function isNpxInvocation(channel = INSTALL_CHANNEL, env = process.env) {
|
|
|
5018
5018
|
function getVersionString() {
|
|
5019
5019
|
return `v${VERSION}`;
|
|
5020
5020
|
}
|
|
5021
|
-
var VERSION = "0.
|
|
5021
|
+
var VERSION = "0.54.0", INSTALL_CHANNEL = "npm";
|
|
5022
5022
|
|
|
5023
5023
|
// src/debug/logger.ts
|
|
5024
5024
|
import {
|
|
@@ -10727,6 +10727,854 @@ var init_sync = __esm(() => {
|
|
|
10727
10727
|
init_watermark();
|
|
10728
10728
|
});
|
|
10729
10729
|
|
|
10730
|
+
// src/telemetry/debug-id.ts
|
|
10731
|
+
var BUNDLE_DEBUG_ID_PLACEHOLDER = "d4304c43-c40e-f448-6475-6e2164756e21";
|
|
10732
|
+
|
|
10733
|
+
// src/telemetry/telemetry.ts
|
|
10734
|
+
import { randomUUID as randomUUID2 } from "node:crypto";
|
|
10735
|
+
import { existsSync as existsSync13 } from "node:fs";
|
|
10736
|
+
import { request as httpRequest2 } from "node:http";
|
|
10737
|
+
import { request as httpsRequest } from "node:https";
|
|
10738
|
+
import { isAbsolute, relative, resolve as resolve2, sep } from "node:path";
|
|
10739
|
+
import { fileURLToPath as fileURLToPath2 } from "node:url";
|
|
10740
|
+
function safeString(value, pattern, fallback) {
|
|
10741
|
+
if (typeof value !== "string" || !pattern.test(value))
|
|
10742
|
+
return fallback;
|
|
10743
|
+
return value;
|
|
10744
|
+
}
|
|
10745
|
+
function canonicalCommand(value) {
|
|
10746
|
+
if (typeof value !== "string")
|
|
10747
|
+
return UNKNOWN_COMMAND;
|
|
10748
|
+
const command = value.trim();
|
|
10749
|
+
if (command.length > 80)
|
|
10750
|
+
return UNKNOWN_COMMAND;
|
|
10751
|
+
if (!/^[a-z][a-z0-9-]*(?: [a-z][a-z0-9-]*){0,3}$/.test(command)) {
|
|
10752
|
+
return UNKNOWN_COMMAND;
|
|
10753
|
+
}
|
|
10754
|
+
return command;
|
|
10755
|
+
}
|
|
10756
|
+
function normalizeContext(context) {
|
|
10757
|
+
const isAuthenticated2 = context?.isAuthenticated === true;
|
|
10758
|
+
const id = safeRead(context?.user, "id");
|
|
10759
|
+
const email = safeRead(context?.user, "email");
|
|
10760
|
+
const orgId = safeRead(context, "orgId");
|
|
10761
|
+
const user = isAuthenticated2 && isUUID(id) ? { id, ...validEmail(email) ? { email } : {} } : undefined;
|
|
10762
|
+
return {
|
|
10763
|
+
mode: context?.mode === "oss" ? "oss" : "cloud",
|
|
10764
|
+
isAuthenticated: isAuthenticated2,
|
|
10765
|
+
...user ? { user } : {},
|
|
10766
|
+
...user && isUUID(orgId) ? { orgId } : {}
|
|
10767
|
+
};
|
|
10768
|
+
}
|
|
10769
|
+
function normalizeExitCode(value, fallback = 1) {
|
|
10770
|
+
if (typeof value !== "number" || !Number.isFinite(value))
|
|
10771
|
+
return fallback;
|
|
10772
|
+
return Math.min(255, Math.max(0, Math.trunc(value)));
|
|
10773
|
+
}
|
|
10774
|
+
function normalizeRuntime(runtime) {
|
|
10775
|
+
return {
|
|
10776
|
+
version: safeString(runtime.version, /^[A-Za-z0-9.+_-]{1,32}$/, "unknown"),
|
|
10777
|
+
installChannel: safeString(runtime.installChannel, /^[A-Za-z][A-Za-z0-9_-]{0,31}$/, "unknown"),
|
|
10778
|
+
platform: safeString(runtime.platform, /^[A-Za-z0-9_-]{1,24}$/, "unknown"),
|
|
10779
|
+
arch: safeString(runtime.arch, /^[A-Za-z0-9_-]{1,24}$/, "unknown"),
|
|
10780
|
+
runtime: safeString(runtime.runtime, /^[A-Za-z][A-Za-z0-9_-]{0,23}$/, "unknown"),
|
|
10781
|
+
runtimeMajor: Math.min(9999, Math.max(0, Math.trunc(runtime.runtimeMajor) || 0)),
|
|
10782
|
+
isCi: runtime.isCi === true,
|
|
10783
|
+
isTty: runtime.isTty === true
|
|
10784
|
+
};
|
|
10785
|
+
}
|
|
10786
|
+
function isUUID(value) {
|
|
10787
|
+
return typeof value === "string" && /^[0-9a-f]{8}-[0-9a-f]{4}-[1-8][0-9a-f]{3}-[89ab][0-9a-f]{3}-[0-9a-f]{12}$/i.test(value);
|
|
10788
|
+
}
|
|
10789
|
+
function validEmail(value) {
|
|
10790
|
+
return typeof value === "string" && value.length <= 254 && /^[A-Za-z0-9.!#$%&'*+/=?^_`{|}~-]+@[A-Za-z0-9](?:[A-Za-z0-9-]{0,61}[A-Za-z0-9])?(?:\.[A-Za-z0-9](?:[A-Za-z0-9-]{0,61}[A-Za-z0-9])?)+$/.test(value);
|
|
10791
|
+
}
|
|
10792
|
+
function validEventId(value) {
|
|
10793
|
+
return typeof value === "string" && /^[0-9a-f]{32}$/i.test(value);
|
|
10794
|
+
}
|
|
10795
|
+
function validDebugId(value) {
|
|
10796
|
+
return typeof value === "string" && /^[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}$/i.test(value);
|
|
10797
|
+
}
|
|
10798
|
+
function validErrorType(value) {
|
|
10799
|
+
return typeof value === "string" && SAFE_ERROR_TYPES.has(value);
|
|
10800
|
+
}
|
|
10801
|
+
function validErrorCode(value) {
|
|
10802
|
+
return typeof value === "string" && SAFE_ERROR_CODES.has(value);
|
|
10803
|
+
}
|
|
10804
|
+
function validStatus(value) {
|
|
10805
|
+
return typeof value === "number" && Number.isInteger(value) && value >= 100 && value <= 599;
|
|
10806
|
+
}
|
|
10807
|
+
function objectLike(value) {
|
|
10808
|
+
return typeof value === "object" && value !== null || typeof value === "function";
|
|
10809
|
+
}
|
|
10810
|
+
function safeRead(value, key) {
|
|
10811
|
+
if (!objectLike(value))
|
|
10812
|
+
return;
|
|
10813
|
+
try {
|
|
10814
|
+
return Reflect.get(value, key);
|
|
10815
|
+
} catch {
|
|
10816
|
+
return;
|
|
10817
|
+
}
|
|
10818
|
+
}
|
|
10819
|
+
function parseStack(stack) {
|
|
10820
|
+
if (typeof stack !== "string" || !CLI_PACKAGE_ROOT)
|
|
10821
|
+
return [];
|
|
10822
|
+
const frames = [];
|
|
10823
|
+
const lines = stack.slice(0, 1e5).split(`
|
|
10824
|
+
`, 200);
|
|
10825
|
+
for (const originalLine of lines) {
|
|
10826
|
+
if (frames.length >= MAX_STACK_FRAMES)
|
|
10827
|
+
break;
|
|
10828
|
+
const parsed = parseOwnedStackLine(originalLine, CLI_PACKAGE_ROOT);
|
|
10829
|
+
if (!parsed)
|
|
10830
|
+
continue;
|
|
10831
|
+
const { filename, lineno, colno } = parsed;
|
|
10832
|
+
if (!Number.isSafeInteger(lineno) || !Number.isSafeInteger(colno) || lineno < 1 || colno < 1 || lineno > 1e7 || colno > 1e7) {
|
|
10833
|
+
continue;
|
|
10834
|
+
}
|
|
10835
|
+
frames.push({ filename, lineno, colno, in_app: true });
|
|
10836
|
+
}
|
|
10837
|
+
return frames;
|
|
10838
|
+
}
|
|
10839
|
+
function detectCliPackageRoot() {
|
|
10840
|
+
try {
|
|
10841
|
+
const modulePath = fileURLToPath2(import.meta.url).replaceAll("\\", "/");
|
|
10842
|
+
for (const suffix of ["/src/telemetry/telemetry.ts", "/bin/dosu.js"]) {
|
|
10843
|
+
if (modulePath.endsWith(suffix))
|
|
10844
|
+
return modulePath.slice(0, -suffix.length);
|
|
10845
|
+
}
|
|
10846
|
+
} catch {}
|
|
10847
|
+
return;
|
|
10848
|
+
}
|
|
10849
|
+
function parseOwnedStackLine(originalLine, packageRoot) {
|
|
10850
|
+
const line = originalLine.replaceAll("\\", "/");
|
|
10851
|
+
const match = line.match(/^\s*at\s+.*\s+\((.+):(\d+):(\d+)\)\s*$/) ?? line.match(/^\s*at\s+(.+):(\d+):(\d+)\s*$/);
|
|
10852
|
+
if (!match?.[1] || !match[2] || !match[3])
|
|
10853
|
+
return;
|
|
10854
|
+
let absolutePath = match[1];
|
|
10855
|
+
try {
|
|
10856
|
+
if (absolutePath.startsWith("file://"))
|
|
10857
|
+
absolutePath = fileURLToPath2(absolutePath);
|
|
10858
|
+
} catch {
|
|
10859
|
+
return;
|
|
10860
|
+
}
|
|
10861
|
+
if (!isAbsolute(absolutePath))
|
|
10862
|
+
return;
|
|
10863
|
+
const resolvedPath = resolve2(absolutePath);
|
|
10864
|
+
const relativePath = relative(packageRoot, resolvedPath);
|
|
10865
|
+
if (relativePath === "" || relativePath === ".." || relativePath.startsWith(`..${sep}`) || isAbsolute(relativePath)) {
|
|
10866
|
+
return;
|
|
10867
|
+
}
|
|
10868
|
+
const filename = relativePath.replaceAll("\\", "/");
|
|
10869
|
+
if (!/^(?:src\/(?:[A-Za-z0-9_.-]+\/)*[A-Za-z0-9_.-]+\.(?:[cm]?[jt]sx?)|bin\/dosu\.js)$/.test(filename)) {
|
|
10870
|
+
return;
|
|
10871
|
+
}
|
|
10872
|
+
try {
|
|
10873
|
+
if (!existsSync13(resolvedPath))
|
|
10874
|
+
return;
|
|
10875
|
+
} catch {
|
|
10876
|
+
return;
|
|
10877
|
+
}
|
|
10878
|
+
return { filename, lineno: Number(match[2]), colno: Number(match[3]) };
|
|
10879
|
+
}
|
|
10880
|
+
function sanitizeError(error) {
|
|
10881
|
+
const normalizedError = domainErrorFromCauseChain(error) ?? error;
|
|
10882
|
+
const data = safeRead(normalizedError, "data");
|
|
10883
|
+
const typeCandidate = safeRead(normalizedError, "name");
|
|
10884
|
+
const codeCandidates = [safeRead(data, "code"), safeRead(normalizedError, "code")];
|
|
10885
|
+
const statusCandidates = [
|
|
10886
|
+
safeRead(data, "httpStatus"),
|
|
10887
|
+
safeRead(normalizedError, "status"),
|
|
10888
|
+
safeRead(normalizedError, "statusCode")
|
|
10889
|
+
];
|
|
10890
|
+
const exitCodeCandidate = safeRead(normalizedError, "exitCode");
|
|
10891
|
+
const code = codeCandidates.find(validErrorCode);
|
|
10892
|
+
const status = statusCandidates.find(validStatus);
|
|
10893
|
+
const exitCode = typeof exitCodeCandidate === "number" && Number.isFinite(exitCodeCandidate) ? normalizeExitCode(exitCodeCandidate) : undefined;
|
|
10894
|
+
return {
|
|
10895
|
+
type: validErrorType(typeCandidate) ? typeCandidate : "Error",
|
|
10896
|
+
...code ? { code } : {},
|
|
10897
|
+
...status ? { status } : {},
|
|
10898
|
+
frames: parseStack(safeRead(normalizedError, "stack")),
|
|
10899
|
+
...exitCode === undefined ? {} : { exitCode }
|
|
10900
|
+
};
|
|
10901
|
+
}
|
|
10902
|
+
function domainErrorFromCauseChain(error) {
|
|
10903
|
+
const seen = new Set;
|
|
10904
|
+
let current = error;
|
|
10905
|
+
for (let depth = 0;depth < 5 && objectLike(current); depth += 1) {
|
|
10906
|
+
if (seen.has(current))
|
|
10907
|
+
return;
|
|
10908
|
+
seen.add(current);
|
|
10909
|
+
const code = safeRead(current, "code");
|
|
10910
|
+
if (code === "SESSION_EXPIRED" || code === "SESSION_PERSISTENCE_ERROR" || code === "SESSION_REFRESH_ERROR") {
|
|
10911
|
+
return current;
|
|
10912
|
+
}
|
|
10913
|
+
current = safeRead(current, "cause");
|
|
10914
|
+
}
|
|
10915
|
+
return;
|
|
10916
|
+
}
|
|
10917
|
+
function durationBucket(durationMs) {
|
|
10918
|
+
const duration = Number.isFinite(durationMs) ? Math.max(0, durationMs) : 0;
|
|
10919
|
+
if (duration < 100)
|
|
10920
|
+
return "<100ms";
|
|
10921
|
+
if (duration < 500)
|
|
10922
|
+
return "100-499ms";
|
|
10923
|
+
if (duration < 2000)
|
|
10924
|
+
return "500ms-1.9s";
|
|
10925
|
+
if (duration < 1e4)
|
|
10926
|
+
return "2-9.9s";
|
|
10927
|
+
if (duration < 60000)
|
|
10928
|
+
return "10-59s";
|
|
10929
|
+
return "60s+";
|
|
10930
|
+
}
|
|
10931
|
+
function countBucket(value) {
|
|
10932
|
+
const n2 = typeof value === "number" && Number.isFinite(value) ? Math.max(0, Math.floor(value)) : 0;
|
|
10933
|
+
if (n2 === 0)
|
|
10934
|
+
return "0";
|
|
10935
|
+
if (n2 < 5)
|
|
10936
|
+
return "1-4";
|
|
10937
|
+
if (n2 < 10)
|
|
10938
|
+
return "5-9";
|
|
10939
|
+
if (n2 < 20)
|
|
10940
|
+
return "10-19";
|
|
10941
|
+
if (n2 < 50)
|
|
10942
|
+
return "20-49";
|
|
10943
|
+
return "50+";
|
|
10944
|
+
}
|
|
10945
|
+
function recordCommandFacets(facets) {
|
|
10946
|
+
pendingFacets = { ...pendingFacets, ...facets };
|
|
10947
|
+
}
|
|
10948
|
+
function consumeCommandFacets() {
|
|
10949
|
+
const facets = pendingFacets;
|
|
10950
|
+
pendingFacets = undefined;
|
|
10951
|
+
return facets;
|
|
10952
|
+
}
|
|
10953
|
+
function allowlisted(value, allowed) {
|
|
10954
|
+
return typeof value === "string" && allowed.has(value) ? value : undefined;
|
|
10955
|
+
}
|
|
10956
|
+
function sanitizeFacets(facets) {
|
|
10957
|
+
if (!facets || typeof facets !== "object")
|
|
10958
|
+
return {};
|
|
10959
|
+
const trigger = allowlisted(facets.sync_trigger, SYNC_TRIGGERS);
|
|
10960
|
+
const status = allowlisted(facets.sync_status, SYNC_STATUSES);
|
|
10961
|
+
const learner = allowlisted(facets.learner_outcome, LEARNER_OUTCOMES);
|
|
10962
|
+
const backfill = allowlisted(facets.backfill_offer, BACKFILL_OFFERS);
|
|
10963
|
+
return {
|
|
10964
|
+
...trigger ? { sync_trigger: trigger } : {},
|
|
10965
|
+
...status ? { sync_status: status } : {},
|
|
10966
|
+
...facets.sessions_studied !== undefined ? { sessions_studied: countBucket(facets.sessions_studied) } : {},
|
|
10967
|
+
...facets.notes_written !== undefined ? { notes_written: countBucket(facets.notes_written) } : {},
|
|
10968
|
+
...learner ? { learner_outcome: learner } : {},
|
|
10969
|
+
...backfill ? { backfill_offer: backfill } : {}
|
|
10970
|
+
};
|
|
10971
|
+
}
|
|
10972
|
+
function buildPostHogPayload(input) {
|
|
10973
|
+
const context = normalizeContext(input.context);
|
|
10974
|
+
const runtime = normalizeRuntime(input.runtime);
|
|
10975
|
+
const errorCode = validErrorCode(input.errorCode) ? input.errorCode : undefined;
|
|
10976
|
+
const facets = sanitizeFacets(input.facets);
|
|
10977
|
+
const result = input.result === "success" || input.result === "validation_error" ? input.result : "failure";
|
|
10978
|
+
return {
|
|
10979
|
+
api_key: input.apiKey,
|
|
10980
|
+
distinct_id: context.user?.id ?? (isUUID(input.installId) ? input.installId : FALLBACK_INSTALL_ID),
|
|
10981
|
+
event: "cli_command_completed",
|
|
10982
|
+
properties: {
|
|
10983
|
+
$geoip_disable: true,
|
|
10984
|
+
...context.user ? {} : { $process_person_profile: false },
|
|
10985
|
+
schema_version: 1,
|
|
10986
|
+
command: canonicalCommand(input.command),
|
|
10987
|
+
result,
|
|
10988
|
+
duration_bucket: durationBucket(input.durationMs),
|
|
10989
|
+
cli_version: runtime.version,
|
|
10990
|
+
install_channel: runtime.installChannel,
|
|
10991
|
+
platform: runtime.platform,
|
|
10992
|
+
arch: runtime.arch,
|
|
10993
|
+
runtime: runtime.runtime,
|
|
10994
|
+
runtime_major: runtime.runtimeMajor,
|
|
10995
|
+
is_ci: runtime.isCi,
|
|
10996
|
+
is_tty: runtime.isTty,
|
|
10997
|
+
mode: context.mode,
|
|
10998
|
+
...context.orgId ? {
|
|
10999
|
+
org_id: context.orgId,
|
|
11000
|
+
$groups: { organization: context.orgId }
|
|
11001
|
+
} : {},
|
|
11002
|
+
is_authenticated: context.isAuthenticated,
|
|
11003
|
+
exit_code: normalizeExitCode(input.exitCode),
|
|
11004
|
+
...errorCode ? { error_code: errorCode } : {},
|
|
11005
|
+
...facets
|
|
11006
|
+
}
|
|
11007
|
+
};
|
|
11008
|
+
}
|
|
11009
|
+
function parsePostHogProjectToken(value) {
|
|
11010
|
+
if (typeof value !== "string")
|
|
11011
|
+
return null;
|
|
11012
|
+
const token = value.trim();
|
|
11013
|
+
return /^phc_[A-Za-z0-9_-]{3,252}$/.test(token) ? token : null;
|
|
11014
|
+
}
|
|
11015
|
+
function parseTelemetryWebAppURL(value, allowInsecureLoopback = false) {
|
|
11016
|
+
if (typeof value !== "string" || value.length === 0 || value.length > 2048)
|
|
11017
|
+
return null;
|
|
11018
|
+
try {
|
|
11019
|
+
const url = new URL(value.trim());
|
|
11020
|
+
if (!url.hostname || url.username || url.password || url.search || url.hash)
|
|
11021
|
+
return null;
|
|
11022
|
+
const loopbackHosts = new Set(["localhost", "127.0.0.1", "[::1]"]);
|
|
11023
|
+
const secure = url.protocol === "https:";
|
|
11024
|
+
const allowedDevelopmentOrigin = allowInsecureLoopback && url.protocol === "http:" && loopbackHosts.has(url.hostname);
|
|
11025
|
+
if (!secure && !allowedDevelopmentOrigin)
|
|
11026
|
+
return null;
|
|
11027
|
+
return url.toString().replace(/\/$/, "");
|
|
11028
|
+
} catch {
|
|
11029
|
+
return null;
|
|
11030
|
+
}
|
|
11031
|
+
}
|
|
11032
|
+
function parseSentryDsn(value) {
|
|
11033
|
+
if (typeof value !== "string" || value.length === 0 || value.length > 2048)
|
|
11034
|
+
return null;
|
|
11035
|
+
const dsn = value.trim();
|
|
11036
|
+
try {
|
|
11037
|
+
const url = new URL(dsn);
|
|
11038
|
+
if (url.protocol !== "https:" || !url.hostname || !url.username || url.password || url.search || url.hash || !/^[A-Za-z0-9_.-]{1,128}$/.test(url.username) || url.username.toLowerCase().startsWith("sntry")) {
|
|
11039
|
+
return null;
|
|
11040
|
+
}
|
|
11041
|
+
const segments = url.pathname.split("/").filter(Boolean);
|
|
11042
|
+
const projectId = segments.pop();
|
|
11043
|
+
if (!projectId || !/^[A-Za-z0-9_-]{1,128}$/.test(projectId))
|
|
11044
|
+
return null;
|
|
11045
|
+
if (segments.some((segment) => !/^[A-Za-z0-9._~-]{1,128}$/.test(segment)))
|
|
11046
|
+
return null;
|
|
11047
|
+
const prefix = segments.length > 0 ? `/${segments.join("/")}` : "";
|
|
11048
|
+
return {
|
|
11049
|
+
dsn,
|
|
11050
|
+
endpoint: `${url.protocol}//${url.host}${prefix}/api/${projectId}/envelope/`,
|
|
11051
|
+
projectId,
|
|
11052
|
+
publicKey: url.username
|
|
11053
|
+
};
|
|
11054
|
+
} catch {
|
|
11055
|
+
return null;
|
|
11056
|
+
}
|
|
11057
|
+
}
|
|
11058
|
+
function sentryTags(command, context, runtime, error) {
|
|
11059
|
+
return {
|
|
11060
|
+
schema_version: "1",
|
|
11061
|
+
command,
|
|
11062
|
+
cli_version: runtime.version,
|
|
11063
|
+
install_channel: runtime.installChannel,
|
|
11064
|
+
os: runtime.platform,
|
|
11065
|
+
arch: runtime.arch,
|
|
11066
|
+
runtime: runtime.runtime,
|
|
11067
|
+
runtime_major: String(runtime.runtimeMajor),
|
|
11068
|
+
is_ci: String(runtime.isCi),
|
|
11069
|
+
is_tty: String(runtime.isTty),
|
|
11070
|
+
mode: context.mode,
|
|
11071
|
+
is_authenticated: String(context.isAuthenticated),
|
|
11072
|
+
...error.code ? { error_code: error.code } : {},
|
|
11073
|
+
...error.status ? { http_status: String(error.status) } : {},
|
|
11074
|
+
...error.exitCode === undefined ? {} : { exit_code: String(error.exitCode) }
|
|
11075
|
+
};
|
|
11076
|
+
}
|
|
11077
|
+
function buildSentryEnvelope(input) {
|
|
11078
|
+
const parsedDsn = parseSentryDsn(input.dsn);
|
|
11079
|
+
if (!parsedDsn || !validEventId(input.eventId))
|
|
11080
|
+
return null;
|
|
11081
|
+
const context = normalizeContext(input.context);
|
|
11082
|
+
const runtime = normalizeRuntime(input.runtime);
|
|
11083
|
+
const command = canonicalCommand(input.command);
|
|
11084
|
+
const debugId = validDebugId(input.debugId) ? input.debugId.toLowerCase() : undefined;
|
|
11085
|
+
const error = {
|
|
11086
|
+
type: validErrorType(input.error.type) ? input.error.type : "Error",
|
|
11087
|
+
...validErrorCode(input.error.code) ? { code: input.error.code } : {},
|
|
11088
|
+
...validStatus(input.error.status) ? { status: input.error.status } : {},
|
|
11089
|
+
...typeof input.error.exitCode === "number" && Number.isFinite(input.error.exitCode) ? { exitCode: normalizeExitCode(input.error.exitCode) } : {},
|
|
11090
|
+
frames: input.error.frames.slice(0, MAX_STACK_FRAMES).map((frame) => {
|
|
11091
|
+
const filename = /^(?:src\/(?:[A-Za-z0-9_.-]+\/)*[A-Za-z0-9_.-]+\.(?:[cm]?[jt]sx?)|bin\/dosu\.js)$/.test(frame.filename) ? frame.filename : "src/unknown.ts";
|
|
11092
|
+
return {
|
|
11093
|
+
filename,
|
|
11094
|
+
...debugId && filename === "bin/dosu.js" ? { abs_path: BUNDLE_CODE_FILE } : {},
|
|
11095
|
+
lineno: Math.min(1e7, Math.max(1, Math.trunc(frame.lineno) || 1)),
|
|
11096
|
+
colno: Math.min(1e7, Math.max(1, Math.trunc(frame.colno) || 1)),
|
|
11097
|
+
in_app: true
|
|
11098
|
+
};
|
|
11099
|
+
}).reverse()
|
|
11100
|
+
};
|
|
11101
|
+
const exceptionValue = {
|
|
11102
|
+
type: error.type,
|
|
11103
|
+
value: error.code ?? error.type,
|
|
11104
|
+
...error.frames.length > 0 ? { stacktrace: { frames: error.frames } } : {}
|
|
11105
|
+
};
|
|
11106
|
+
const newestFrame = error.frames.at(-1);
|
|
11107
|
+
const safeCallsite = newestFrame ? `${newestFrame.filename}:${newestFrame.lineno}` : "unknown";
|
|
11108
|
+
const timestampMs = Number.isFinite(input.timestampMs) ? Math.max(0, input.timestampMs) : 0;
|
|
11109
|
+
const mappedBundle = debugId && error.frames.some((frame) => frame.abs_path === BUNDLE_CODE_FILE);
|
|
11110
|
+
const event = {
|
|
11111
|
+
event_id: input.eventId.toLowerCase(),
|
|
11112
|
+
timestamp: timestampMs / 1000,
|
|
11113
|
+
platform: "node",
|
|
11114
|
+
level: "error",
|
|
11115
|
+
release: `dosu-cli@${runtime.version}`,
|
|
11116
|
+
tags: sentryTags(command, context, runtime, error),
|
|
11117
|
+
...context.user ? {
|
|
11118
|
+
user: {
|
|
11119
|
+
id: context.user.id,
|
|
11120
|
+
...context.user.email ? { email: context.user.email } : {}
|
|
11121
|
+
}
|
|
11122
|
+
} : {},
|
|
11123
|
+
...mappedBundle ? {
|
|
11124
|
+
debug_meta: {
|
|
11125
|
+
images: [{ type: "sourcemap", code_file: BUNDLE_CODE_FILE, debug_id: debugId }]
|
|
11126
|
+
}
|
|
11127
|
+
} : {},
|
|
11128
|
+
fingerprint: ["dosu-cli", command, error.type, error.code ?? "unknown", safeCallsite],
|
|
11129
|
+
exception: { values: [exceptionValue] }
|
|
11130
|
+
};
|
|
11131
|
+
const envelopeHeader = {
|
|
11132
|
+
event_id: input.eventId.toLowerCase(),
|
|
11133
|
+
dsn: parsedDsn.dsn,
|
|
11134
|
+
sent_at: new Date(timestampMs).toISOString()
|
|
11135
|
+
};
|
|
11136
|
+
return {
|
|
11137
|
+
endpoint: parsedDsn.endpoint,
|
|
11138
|
+
body: `${JSON.stringify(envelopeHeader)}
|
|
11139
|
+
${JSON.stringify({ type: "event" })}
|
|
11140
|
+
${JSON.stringify(event)}`
|
|
11141
|
+
};
|
|
11142
|
+
}
|
|
11143
|
+
function fetchWithoutRedirect(url, init = {}) {
|
|
11144
|
+
let parsedURL;
|
|
11145
|
+
try {
|
|
11146
|
+
parsedURL = new URL(url);
|
|
11147
|
+
if (parsedURL.protocol !== "http:" && parsedURL.protocol !== "https:") {
|
|
11148
|
+
return Promise.reject(new Error("unsupported telemetry protocol"));
|
|
11149
|
+
}
|
|
11150
|
+
} catch (error) {
|
|
11151
|
+
return Promise.reject(error);
|
|
11152
|
+
}
|
|
11153
|
+
return new Promise((resolve3, reject) => {
|
|
11154
|
+
let request;
|
|
11155
|
+
let response;
|
|
11156
|
+
let settled = false;
|
|
11157
|
+
const signal = init.signal;
|
|
11158
|
+
const cleanup = () => signal?.removeEventListener("abort", onAbort);
|
|
11159
|
+
const fail = (error) => {
|
|
11160
|
+
if (settled)
|
|
11161
|
+
return;
|
|
11162
|
+
settled = true;
|
|
11163
|
+
cleanup();
|
|
11164
|
+
response?.destroy();
|
|
11165
|
+
request?.socket?.destroy();
|
|
11166
|
+
request?.destroy();
|
|
11167
|
+
reject(error);
|
|
11168
|
+
};
|
|
11169
|
+
const succeed = (value) => {
|
|
11170
|
+
if (settled)
|
|
11171
|
+
return;
|
|
11172
|
+
settled = true;
|
|
11173
|
+
cleanup();
|
|
11174
|
+
response?.destroy();
|
|
11175
|
+
request?.socket?.destroy();
|
|
11176
|
+
request?.destroy();
|
|
11177
|
+
resolve3(value);
|
|
11178
|
+
};
|
|
11179
|
+
const onAbort = () => {
|
|
11180
|
+
const error = new Error("telemetry request aborted");
|
|
11181
|
+
error.name = "AbortError";
|
|
11182
|
+
fail(error);
|
|
11183
|
+
};
|
|
11184
|
+
try {
|
|
11185
|
+
if (signal?.aborted) {
|
|
11186
|
+
onAbort();
|
|
11187
|
+
return;
|
|
11188
|
+
}
|
|
11189
|
+
const body = init.body;
|
|
11190
|
+
if (body !== undefined && body !== null && typeof body !== "string") {
|
|
11191
|
+
fail(new Error("unsupported telemetry request body"));
|
|
11192
|
+
return;
|
|
11193
|
+
}
|
|
11194
|
+
const headers = Object.fromEntries(new Headers(init.headers).entries());
|
|
11195
|
+
const transport = parsedURL.protocol === "https:" ? httpsRequest : httpRequest2;
|
|
11196
|
+
request = transport(parsedURL, {
|
|
11197
|
+
method: init.method ?? "GET",
|
|
11198
|
+
headers,
|
|
11199
|
+
agent: false
|
|
11200
|
+
}, (incoming) => {
|
|
11201
|
+
response = incoming;
|
|
11202
|
+
const chunks = [];
|
|
11203
|
+
let totalBytes = 0;
|
|
11204
|
+
incoming.on("data", (chunk) => {
|
|
11205
|
+
const bytes = Buffer.isBuffer(chunk) ? chunk : Buffer.from(chunk);
|
|
11206
|
+
totalBytes += bytes.byteLength;
|
|
11207
|
+
if (totalBytes > MAX_RESPONSE_BYTES) {
|
|
11208
|
+
fail(new Error("telemetry response too large"));
|
|
11209
|
+
return;
|
|
11210
|
+
}
|
|
11211
|
+
chunks.push(bytes);
|
|
11212
|
+
});
|
|
11213
|
+
incoming.once("error", fail);
|
|
11214
|
+
incoming.once("end", () => {
|
|
11215
|
+
try {
|
|
11216
|
+
const responseHeaders = new Headers;
|
|
11217
|
+
for (let index = 0;index < incoming.rawHeaders.length; index += 2) {
|
|
11218
|
+
const name = incoming.rawHeaders[index];
|
|
11219
|
+
const value = incoming.rawHeaders[index + 1];
|
|
11220
|
+
if (name && value)
|
|
11221
|
+
responseHeaders.append(name, value);
|
|
11222
|
+
}
|
|
11223
|
+
const status = incoming.statusCode;
|
|
11224
|
+
if (!status)
|
|
11225
|
+
throw new Error("telemetry response missing status");
|
|
11226
|
+
succeed(new Response(chunks.length > 0 ? Buffer.concat(chunks) : null, {
|
|
11227
|
+
status,
|
|
11228
|
+
statusText: incoming.statusMessage,
|
|
11229
|
+
headers: responseHeaders
|
|
11230
|
+
}));
|
|
11231
|
+
} catch (error) {
|
|
11232
|
+
fail(error);
|
|
11233
|
+
}
|
|
11234
|
+
});
|
|
11235
|
+
});
|
|
11236
|
+
request.once("error", fail);
|
|
11237
|
+
signal?.addEventListener("abort", onAbort, { once: true });
|
|
11238
|
+
request.end(body);
|
|
11239
|
+
} catch (error) {
|
|
11240
|
+
fail(error);
|
|
11241
|
+
}
|
|
11242
|
+
});
|
|
11243
|
+
}
|
|
11244
|
+
async function sendHttpsRequest(url, init, fetcher = fetchWithoutRedirect) {
|
|
11245
|
+
try {
|
|
11246
|
+
if (new URL(url).protocol !== "https:")
|
|
11247
|
+
return false;
|
|
11248
|
+
} catch {
|
|
11249
|
+
return false;
|
|
11250
|
+
}
|
|
11251
|
+
const controller = new AbortController;
|
|
11252
|
+
let timer;
|
|
11253
|
+
const timeout = new Promise((_resolve, reject) => {
|
|
11254
|
+
timer = setTimeout(() => {
|
|
11255
|
+
controller.abort();
|
|
11256
|
+
reject(new Error("telemetry request timed out"));
|
|
11257
|
+
}, REQUEST_TIMEOUT_MS);
|
|
11258
|
+
});
|
|
11259
|
+
try {
|
|
11260
|
+
const request = fetcher(url, { ...init, redirect: "error", signal: controller.signal });
|
|
11261
|
+
const result = await Promise.race([request, timeout]);
|
|
11262
|
+
return result.ok;
|
|
11263
|
+
} catch {
|
|
11264
|
+
return false;
|
|
11265
|
+
} finally {
|
|
11266
|
+
controller.abort();
|
|
11267
|
+
if (timer !== undefined)
|
|
11268
|
+
clearTimeout(timer);
|
|
11269
|
+
}
|
|
11270
|
+
}
|
|
11271
|
+
function environmentFlag(value) {
|
|
11272
|
+
if (value === undefined)
|
|
11273
|
+
return false;
|
|
11274
|
+
const normalized = value.trim().toLowerCase();
|
|
11275
|
+
return normalized !== "" && !["0", "false", "no", "off"].includes(normalized);
|
|
11276
|
+
}
|
|
11277
|
+
function runtimeNameAndMajor() {
|
|
11278
|
+
const versions = process.versions;
|
|
11279
|
+
const runtime = versions.bun ? "bun" : "node";
|
|
11280
|
+
const version = versions.bun ?? versions.node;
|
|
11281
|
+
return { runtime, runtimeMajor: Number.parseInt(version.split(".")[0] ?? "0", 10) || 0 };
|
|
11282
|
+
}
|
|
11283
|
+
function resolveRuntime(dependencies, env) {
|
|
11284
|
+
const detected = runtimeNameAndMajor();
|
|
11285
|
+
return normalizeRuntime({
|
|
11286
|
+
version: dependencies.version ?? VERSION,
|
|
11287
|
+
installChannel: dependencies.installChannel ?? INSTALL_CHANNEL,
|
|
11288
|
+
platform: dependencies.platform ?? process.platform,
|
|
11289
|
+
arch: dependencies.arch ?? process.arch,
|
|
11290
|
+
runtime: dependencies.runtime ?? detected.runtime,
|
|
11291
|
+
runtimeMajor: dependencies.runtimeMajor ?? detected.runtimeMajor,
|
|
11292
|
+
isCi: dependencies.isCi ?? environmentFlag(env.CI),
|
|
11293
|
+
isTty: dependencies.isTty ?? process.stdout.isTTY === true
|
|
11294
|
+
});
|
|
11295
|
+
}
|
|
11296
|
+
function validationFailure(exitCode, error) {
|
|
11297
|
+
if (exitCode === 2)
|
|
11298
|
+
return true;
|
|
11299
|
+
if (error?.type === "CliUsageError")
|
|
11300
|
+
return true;
|
|
11301
|
+
if (!error?.code)
|
|
11302
|
+
return false;
|
|
11303
|
+
return error.code.startsWith("commander.") || ["BAD_REQUEST", "INVALID_ARGUMENT", "UNPROCESSABLE_CONTENT", "VALIDATION_ERROR"].includes(error.code);
|
|
11304
|
+
}
|
|
11305
|
+
function safeNow(now) {
|
|
11306
|
+
try {
|
|
11307
|
+
const value = now();
|
|
11308
|
+
return Number.isFinite(value) ? Math.max(0, value) : 0;
|
|
11309
|
+
} catch {
|
|
11310
|
+
return 0;
|
|
11311
|
+
}
|
|
11312
|
+
}
|
|
11313
|
+
function safeUuid(generate) {
|
|
11314
|
+
try {
|
|
11315
|
+
const value = generate();
|
|
11316
|
+
return isUUID(value) ? value : undefined;
|
|
11317
|
+
} catch {
|
|
11318
|
+
return;
|
|
11319
|
+
}
|
|
11320
|
+
}
|
|
11321
|
+
function eventId(generate) {
|
|
11322
|
+
const uuid2 = safeUuid(generate);
|
|
11323
|
+
return uuid2?.replaceAll("-", "");
|
|
11324
|
+
}
|
|
11325
|
+
function createCommandTelemetry(settings2, dependencies = {}) {
|
|
11326
|
+
const env = dependencies.env ?? process.env;
|
|
11327
|
+
const fetcher = dependencies.fetch;
|
|
11328
|
+
const now = dependencies.now ?? Date.now;
|
|
11329
|
+
const generateUuid = dependencies.randomUUID ?? randomUUID2;
|
|
11330
|
+
const writeStderr = dependencies.stderr ?? ((payload) => process.stderr.write(`${payload}
|
|
11331
|
+
`));
|
|
11332
|
+
const webAppURL = dependencies.webAppURL ?? getWebAppURL;
|
|
11333
|
+
const resolveFacets = dependencies.facets ?? consumeCommandFacets;
|
|
11334
|
+
const runtime = resolveRuntime(dependencies, env);
|
|
11335
|
+
const debug = env.DOSU_TELEMETRY_DEBUG === "1";
|
|
11336
|
+
const disabled = settings2.disabled === true || environmentFlag(env.DO_NOT_TRACK) || environmentFlag(env.DOSU_TELEMETRY_DISABLED);
|
|
11337
|
+
const rawAnalyticsToken = dependencies.env ? env.DOSU_POSTHOG_PROJECT_TOKEN_OVERRIDE ?? env.DOSU_POSTHOG_PROJECT_TOKEN : process.env.DOSU_POSTHOG_PROJECT_TOKEN_OVERRIDE ?? "phc_Uo9RxhN0HRk8gZkPlT18BCr7eqsM0MPWVW88HZB95V5";
|
|
11338
|
+
const rawSentryDsn = dependencies.env ? env.DOSU_CLI_SENTRY_DSN_OVERRIDE ?? env.DOSU_CLI_SENTRY_DSN : process.env.DOSU_CLI_SENTRY_DSN_OVERRIDE ?? "https://f6b3e51dea6f50746316aa1896df76e0@o4505364738670592.ingest.us.sentry.io/4511883653283840";
|
|
11339
|
+
const analyticsToken = parsePostHogProjectToken(rawAnalyticsToken) ?? undefined;
|
|
11340
|
+
const sentryDsn = parseSentryDsn(rawSentryDsn)?.dsn;
|
|
11341
|
+
let started = false;
|
|
11342
|
+
let terminal = false;
|
|
11343
|
+
let startedAt = 0;
|
|
11344
|
+
let command = UNKNOWN_COMMAND;
|
|
11345
|
+
let context = normalizeContext(undefined);
|
|
11346
|
+
let installIdResolved = false;
|
|
11347
|
+
let resolvedInstallId = settings2.install_id;
|
|
11348
|
+
function installId() {
|
|
11349
|
+
if (isUUID(resolvedInstallId))
|
|
11350
|
+
return resolvedInstallId;
|
|
11351
|
+
if (installIdResolved)
|
|
11352
|
+
return;
|
|
11353
|
+
installIdResolved = true;
|
|
11354
|
+
try {
|
|
11355
|
+
resolvedInstallId = dependencies.resolveInstallId?.();
|
|
11356
|
+
} catch {
|
|
11357
|
+
resolvedInstallId = undefined;
|
|
11358
|
+
}
|
|
11359
|
+
return isUUID(resolvedInstallId) ? resolvedInstallId : undefined;
|
|
11360
|
+
}
|
|
11361
|
+
function debugPayload(payload) {
|
|
11362
|
+
try {
|
|
11363
|
+
writeStderr(payload);
|
|
11364
|
+
} catch {}
|
|
11365
|
+
}
|
|
11366
|
+
async function dispatch(result, exitCode, error) {
|
|
11367
|
+
try {
|
|
11368
|
+
const tasks = [];
|
|
11369
|
+
if (!disabled && analyticsToken) {
|
|
11370
|
+
const id = context.user?.id ?? installId();
|
|
11371
|
+
if (id) {
|
|
11372
|
+
let facets;
|
|
11373
|
+
try {
|
|
11374
|
+
facets = resolveFacets();
|
|
11375
|
+
} catch {
|
|
11376
|
+
facets = undefined;
|
|
11377
|
+
}
|
|
11378
|
+
const payload = buildPostHogPayload({
|
|
11379
|
+
apiKey: analyticsToken,
|
|
11380
|
+
...context.user ? {} : { installId: id },
|
|
11381
|
+
command,
|
|
11382
|
+
result,
|
|
11383
|
+
durationMs: safeNow(now) - startedAt,
|
|
11384
|
+
exitCode,
|
|
11385
|
+
errorCode: error?.code,
|
|
11386
|
+
...facets ? { facets } : {},
|
|
11387
|
+
context,
|
|
11388
|
+
runtime
|
|
11389
|
+
});
|
|
11390
|
+
const body = JSON.stringify(payload);
|
|
11391
|
+
if (debug) {
|
|
11392
|
+
debugPayload(body);
|
|
11393
|
+
} else {
|
|
11394
|
+
const base = parseTelemetryWebAppURL(webAppURL());
|
|
11395
|
+
if (base) {
|
|
11396
|
+
tasks.push(sendHttpsRequest(`${base}/ph-api/i/v0/e/`, {
|
|
11397
|
+
method: "POST",
|
|
11398
|
+
headers: { "content-type": "application/json" },
|
|
11399
|
+
body
|
|
11400
|
+
}, fetcher));
|
|
11401
|
+
}
|
|
11402
|
+
}
|
|
11403
|
+
}
|
|
11404
|
+
}
|
|
11405
|
+
if (!disabled && result === "failure" && error && shouldSendToSentry(error) && sentryDsn) {
|
|
11406
|
+
const id = eventId(generateUuid);
|
|
11407
|
+
const envelope = id ? buildSentryEnvelope({
|
|
11408
|
+
dsn: sentryDsn,
|
|
11409
|
+
command,
|
|
11410
|
+
context,
|
|
11411
|
+
runtime,
|
|
11412
|
+
error,
|
|
11413
|
+
eventId: id,
|
|
11414
|
+
timestampMs: safeNow(now),
|
|
11415
|
+
debugId: BUNDLE_DEBUG_ID_PLACEHOLDER
|
|
11416
|
+
}) : null;
|
|
11417
|
+
if (envelope) {
|
|
11418
|
+
if (debug) {
|
|
11419
|
+
debugPayload(envelope.body);
|
|
11420
|
+
} else {
|
|
11421
|
+
tasks.push(sendHttpsRequest(envelope.endpoint, {
|
|
11422
|
+
method: "POST",
|
|
11423
|
+
headers: { "content-type": "application/x-sentry-envelope" },
|
|
11424
|
+
body: envelope.body
|
|
11425
|
+
}, fetcher));
|
|
11426
|
+
}
|
|
11427
|
+
}
|
|
11428
|
+
}
|
|
11429
|
+
await Promise.all(tasks);
|
|
11430
|
+
} catch {}
|
|
11431
|
+
}
|
|
11432
|
+
return {
|
|
11433
|
+
start(commandName, commandContext) {
|
|
11434
|
+
if (started || terminal)
|
|
11435
|
+
return;
|
|
11436
|
+
started = true;
|
|
11437
|
+
command = canonicalCommand(commandName);
|
|
11438
|
+
context = normalizeContext(commandContext);
|
|
11439
|
+
startedAt = safeNow(now);
|
|
11440
|
+
},
|
|
11441
|
+
async complete(exitCode = 0) {
|
|
11442
|
+
if (!started || terminal)
|
|
11443
|
+
return;
|
|
11444
|
+
terminal = true;
|
|
11445
|
+
const normalizedExitCode = normalizeExitCode(exitCode, 0);
|
|
11446
|
+
const result = normalizedExitCode === 0 ? "success" : validationFailure(normalizedExitCode, undefined) ? "validation_error" : "failure";
|
|
11447
|
+
const error = result === "failure" ? { type: "CommandExitError", frames: [], exitCode: normalizedExitCode } : undefined;
|
|
11448
|
+
await dispatch(result, normalizedExitCode, error);
|
|
11449
|
+
},
|
|
11450
|
+
async fail(rawError) {
|
|
11451
|
+
if (!started || terminal)
|
|
11452
|
+
return;
|
|
11453
|
+
terminal = true;
|
|
11454
|
+
const error = sanitizeError(rawError);
|
|
11455
|
+
const exitCode = error.exitCode ?? 1;
|
|
11456
|
+
const result = validationFailure(exitCode, error) ? "validation_error" : "failure";
|
|
11457
|
+
await dispatch(result, exitCode, error);
|
|
11458
|
+
}
|
|
11459
|
+
};
|
|
11460
|
+
}
|
|
11461
|
+
function shouldSendToSentry(error) {
|
|
11462
|
+
return !["SESSION_EXPIRED", "SESSION_PERSISTENCE_ERROR"].includes(error.code ?? "");
|
|
11463
|
+
}
|
|
11464
|
+
var REQUEST_TIMEOUT_MS = 500, MAX_RESPONSE_BYTES, MAX_STACK_FRAMES = 20, UNKNOWN_COMMAND = "unknown", FALLBACK_INSTALL_ID = "00000000-0000-4000-8000-000000000000", BUNDLE_CODE_FILE = "app:///bin/dosu.js", CLI_PACKAGE_ROOT, SAFE_ERROR_TYPES, SAFE_ERROR_CODES, SYNC_TRIGGERS, SYNC_STATUSES, LEARNER_OUTCOMES, BACKFILL_OFFERS, pendingFacets;
|
|
11465
|
+
var init_telemetry = __esm(() => {
|
|
11466
|
+
MAX_RESPONSE_BYTES = 1024 * 1024;
|
|
11467
|
+
CLI_PACKAGE_ROOT = detectCliPackageRoot();
|
|
11468
|
+
SAFE_ERROR_TYPES = new Set([
|
|
11469
|
+
"AbortError",
|
|
11470
|
+
"AggregateError",
|
|
11471
|
+
"CommanderError",
|
|
11472
|
+
"CliUsageError",
|
|
11473
|
+
"CommandExitError",
|
|
11474
|
+
"Error",
|
|
11475
|
+
"OAuthCallbackError",
|
|
11476
|
+
"RangeError",
|
|
11477
|
+
"ReferenceError",
|
|
11478
|
+
"SessionExpiredError",
|
|
11479
|
+
"SessionPersistenceError",
|
|
11480
|
+
"SessionRefreshError",
|
|
11481
|
+
"SyntaxError",
|
|
11482
|
+
"TRPCClientError",
|
|
11483
|
+
"TypeError"
|
|
11484
|
+
]);
|
|
11485
|
+
SAFE_ERROR_CODES = new Set([
|
|
11486
|
+
"ABORT_ERR",
|
|
11487
|
+
"BAD_GATEWAY",
|
|
11488
|
+
"BAD_REQUEST",
|
|
11489
|
+
"CLIENT_CLOSED_REQUEST",
|
|
11490
|
+
"CONFLICT",
|
|
11491
|
+
"EACCES",
|
|
11492
|
+
"EAI_AGAIN",
|
|
11493
|
+
"EBUSY",
|
|
11494
|
+
"ECONNREFUSED",
|
|
11495
|
+
"ECONNRESET",
|
|
11496
|
+
"EEXIST",
|
|
11497
|
+
"EHOSTUNREACH",
|
|
11498
|
+
"EINVAL",
|
|
11499
|
+
"EISDIR",
|
|
11500
|
+
"ELOOP",
|
|
11501
|
+
"EMFILE",
|
|
11502
|
+
"ENAMETOOLONG",
|
|
11503
|
+
"ENETUNREACH",
|
|
11504
|
+
"ENFILE",
|
|
11505
|
+
"ENOENT",
|
|
11506
|
+
"ENOMEM",
|
|
11507
|
+
"ENOSPC",
|
|
11508
|
+
"ENOTFOUND",
|
|
11509
|
+
"ENOTDIR",
|
|
11510
|
+
"EPIPE",
|
|
11511
|
+
"EPERM",
|
|
11512
|
+
"EROFS",
|
|
11513
|
+
"ETIMEDOUT",
|
|
11514
|
+
"FORBIDDEN",
|
|
11515
|
+
"GATEWAY_TIMEOUT",
|
|
11516
|
+
"IMPORT_ALREADY_IN_PROGRESS",
|
|
11517
|
+
"INTERNAL_SERVER_ERROR",
|
|
11518
|
+
"INVALID_ARGUMENT",
|
|
11519
|
+
"METHOD_NOT_SUPPORTED",
|
|
11520
|
+
"NOT_FOUND",
|
|
11521
|
+
"NOT_IMPLEMENTED",
|
|
11522
|
+
"PARSE_ERROR",
|
|
11523
|
+
"PAYLOAD_TOO_LARGE",
|
|
11524
|
+
"PAYMENT_REQUIRED",
|
|
11525
|
+
"PRECONDITION_FAILED",
|
|
11526
|
+
"SERVICE_UNAVAILABLE",
|
|
11527
|
+
"SESSION_EXPIRED",
|
|
11528
|
+
"SESSION_PERSISTENCE_ERROR",
|
|
11529
|
+
"SESSION_REFRESH_ERROR",
|
|
11530
|
+
"TIMEOUT",
|
|
11531
|
+
"TOO_MANY_REQUESTS",
|
|
11532
|
+
"UNAUTHORIZED",
|
|
11533
|
+
"UNPROCESSABLE_CONTENT",
|
|
11534
|
+
"UNSUPPORTED_MEDIA_TYPE",
|
|
11535
|
+
"VALIDATION_ERROR",
|
|
11536
|
+
"commander.conflictingOption",
|
|
11537
|
+
"commander.error",
|
|
11538
|
+
"commander.excessArguments",
|
|
11539
|
+
"commander.invalidArgument",
|
|
11540
|
+
"commander.missingArgument",
|
|
11541
|
+
"commander.missingMandatoryOptionValue",
|
|
11542
|
+
"commander.optionMissingArgument",
|
|
11543
|
+
"commander.unknownCommand",
|
|
11544
|
+
"commander.unknownOption"
|
|
11545
|
+
]);
|
|
11546
|
+
SYNC_TRIGGERS = new Set(["hook", "manual", "bootstrap"]);
|
|
11547
|
+
SYNC_STATUSES = new Set([
|
|
11548
|
+
"backlog",
|
|
11549
|
+
"nothing-new",
|
|
11550
|
+
"skipped-backoff",
|
|
11551
|
+
"skipped-lock",
|
|
11552
|
+
"skipped-gateway",
|
|
11553
|
+
"skipped-paused",
|
|
11554
|
+
"studied",
|
|
11555
|
+
"mine-failed",
|
|
11556
|
+
"error",
|
|
11557
|
+
"detached",
|
|
11558
|
+
"detach-failed",
|
|
11559
|
+
"status-only"
|
|
11560
|
+
]);
|
|
11561
|
+
LEARNER_OUTCOMES = new Set([
|
|
11562
|
+
"completed",
|
|
11563
|
+
"settings_conflict",
|
|
11564
|
+
"consent_off",
|
|
11565
|
+
"credit_limit",
|
|
11566
|
+
"quota_exceeded",
|
|
11567
|
+
"error"
|
|
11568
|
+
]);
|
|
11569
|
+
BACKFILL_OFFERS = new Set([
|
|
11570
|
+
"not-offered",
|
|
11571
|
+
"accepted",
|
|
11572
|
+
"declined",
|
|
11573
|
+
"cancelled",
|
|
11574
|
+
"spawn-failed"
|
|
11575
|
+
]);
|
|
11576
|
+
});
|
|
11577
|
+
|
|
10730
11578
|
// src/report/backfill.ts
|
|
10731
11579
|
function noteTime2(at) {
|
|
10732
11580
|
const parsed = Date.parse(at ?? "");
|
|
@@ -10872,7 +11720,7 @@ function createRunConfigDir() {
|
|
|
10872
11720
|
var init_config_dir = () => {};
|
|
10873
11721
|
|
|
10874
11722
|
// src/learner/conflicts.ts
|
|
10875
|
-
import { existsSync as
|
|
11723
|
+
import { existsSync as existsSync14, readFileSync as readFileSync15 } from "node:fs";
|
|
10876
11724
|
function managedSettingsPaths(platform3 = process.platform) {
|
|
10877
11725
|
switch (platform3) {
|
|
10878
11726
|
case "darwin":
|
|
@@ -10901,7 +11749,7 @@ function conflictingKeysIn(settings2) {
|
|
|
10901
11749
|
function detectSettingsConflicts(paths = managedSettingsPaths()) {
|
|
10902
11750
|
const conflicts = [];
|
|
10903
11751
|
for (const file of paths) {
|
|
10904
|
-
if (!
|
|
11752
|
+
if (!existsSync14(file))
|
|
10905
11753
|
continue;
|
|
10906
11754
|
let settings2;
|
|
10907
11755
|
try {
|
|
@@ -10964,7 +11812,7 @@ var init_env = __esm(() => {
|
|
|
10964
11812
|
});
|
|
10965
11813
|
|
|
10966
11814
|
// src/learner/executable.ts
|
|
10967
|
-
import { accessSync, constants, existsSync as
|
|
11815
|
+
import { accessSync, constants, existsSync as existsSync15 } from "node:fs";
|
|
10968
11816
|
import { createRequire as createRequire3 } from "node:module";
|
|
10969
11817
|
import { homedir as homedir4 } from "node:os";
|
|
10970
11818
|
import { delimiter as delimiter2, dirname as dirname5, join as join14 } from "node:path";
|
|
@@ -10976,7 +11824,7 @@ function sdkNativeBinaryExists() {
|
|
|
10976
11824
|
const requireRuntime = createRequire3(import.meta.url);
|
|
10977
11825
|
const pkg = `@anthropic-ai/claude-agent-sdk-${process.platform}-${process.arch}`;
|
|
10978
11826
|
const pkgDir = dirname5(requireRuntime.resolve(`${pkg}/package.json`));
|
|
10979
|
-
return binaryNames().some((name) =>
|
|
11827
|
+
return binaryNames().some((name) => existsSync15(join14(pkgDir, name)));
|
|
10980
11828
|
} catch {
|
|
10981
11829
|
return false;
|
|
10982
11830
|
}
|
|
@@ -73091,1246 +73939,479 @@ async function runLearner(options) {
|
|
|
73091
73939
|
const gatewayError = classifyGatewayError(text);
|
|
73092
73940
|
if (gatewayError) {
|
|
73093
73941
|
return { outcome: gatewayError.outcome, notesWritten, turns, message: gatewayError.message };
|
|
73094
|
-
}
|
|
73095
|
-
logger.debug("learner", `run ${runID} threw: ${text}`);
|
|
73096
|
-
return {
|
|
73097
|
-
outcome: "error",
|
|
73098
|
-
notesWritten,
|
|
73099
|
-
turns,
|
|
73100
|
-
message: abort.signal.aborted ? "Study run timed out and was aborted." : "Study run failed; see debug log for details."
|
|
73101
|
-
};
|
|
73102
|
-
} finally {
|
|
73103
|
-
clearTimeout(timer);
|
|
73104
|
-
configDir.cleanup();
|
|
73105
|
-
}
|
|
73106
|
-
}
|
|
73107
|
-
var DEFAULT_MAX_TURNS = 160, DEFAULT_MAX_NOTES = 80, DEFAULT_TIMEOUT_MS2, KNOWLEDGE_SERVER_NAME = "dosu", GATEWAY_ERRORS, TRACE_SNIPPET_LIMIT = 400;
|
|
73108
|
-
var init_runner = __esm(() => {
|
|
73109
|
-
init_logger();
|
|
73110
|
-
init_config_helpers();
|
|
73111
|
-
init_notes();
|
|
73112
|
-
init_config_dir();
|
|
73113
|
-
init_conflicts();
|
|
73114
|
-
init_env();
|
|
73115
|
-
init_executable();
|
|
73116
|
-
init_tools();
|
|
73117
|
-
DEFAULT_TIMEOUT_MS2 = 30 * 60 * 1000;
|
|
73118
|
-
GATEWAY_ERRORS = {
|
|
73119
|
-
dosu_consent_off: {
|
|
73120
|
-
outcome: "consent_off",
|
|
73121
|
-
message: "Your org hasn't enabled Dosu Remote Sessions. Ask an org admin to turn it on."
|
|
73122
|
-
},
|
|
73123
|
-
dosu_credit_limit_reached: {
|
|
73124
|
-
outcome: "credit_limit",
|
|
73125
|
-
message: "Your org has used its Dosu credits for this billing period. An admin can enable overage or upgrade."
|
|
73126
|
-
},
|
|
73127
|
-
dosu_quota_exceeded: {
|
|
73128
|
-
outcome: "quota_exceeded",
|
|
73129
|
-
message: "Daily Dosu Remote Sessions budget reached; runs resume tomorrow."
|
|
73130
|
-
}
|
|
73131
|
-
};
|
|
73132
|
-
});
|
|
73133
|
-
|
|
73134
|
-
// src/version/skill-update-check.ts
|
|
73135
|
-
import { existsSync as existsSync17, mkdirSync as mkdirSync10, readFileSync as readFileSync18, writeFileSync as writeFileSync11 } from "node:fs";
|
|
73136
|
-
import { join as join20 } from "node:path";
|
|
73137
|
-
function getCachePath2() {
|
|
73138
|
-
return join20(getConfigDir(), CACHE_FILENAME3);
|
|
73139
|
-
}
|
|
73140
|
-
function readSkillCache() {
|
|
73141
|
-
try {
|
|
73142
|
-
const path3 = getCachePath2();
|
|
73143
|
-
if (!existsSync17(path3))
|
|
73144
|
-
return null;
|
|
73145
|
-
const data = JSON.parse(readFileSync18(path3, "utf-8"));
|
|
73146
|
-
if (typeof data.lastCheck === "number" && typeof data.latestSha === "string" && typeof data.installedSha === "string") {
|
|
73147
|
-
return data;
|
|
73148
|
-
}
|
|
73149
|
-
return null;
|
|
73150
|
-
} catch {
|
|
73151
|
-
return null;
|
|
73152
|
-
}
|
|
73153
|
-
}
|
|
73154
|
-
function writeSkillCache(cache) {
|
|
73155
|
-
try {
|
|
73156
|
-
const dir = getConfigDir();
|
|
73157
|
-
if (!existsSync17(dir)) {
|
|
73158
|
-
mkdirSync10(dir, { recursive: true, mode: 448 });
|
|
73159
|
-
}
|
|
73160
|
-
writeFileSync11(getCachePath2(), JSON.stringify(cache), { mode: 384 });
|
|
73161
|
-
} catch {}
|
|
73162
|
-
}
|
|
73163
|
-
function clearInstalledSha() {
|
|
73164
|
-
const cache = readSkillCache();
|
|
73165
|
-
if (cache)
|
|
73166
|
-
writeSkillCache({ ...cache, installedSha: "" });
|
|
73167
|
-
}
|
|
73168
|
-
async function fetchLatestSha() {
|
|
73169
|
-
const controller = new AbortController;
|
|
73170
|
-
const timeout = setTimeout(() => controller.abort(), FETCH_TIMEOUT_MS2);
|
|
73171
|
-
try {
|
|
73172
|
-
const resp = await fetch(GITHUB_API_URL, {
|
|
73173
|
-
signal: controller.signal,
|
|
73174
|
-
headers: {
|
|
73175
|
-
"User-Agent": `dosu-cli/${VERSION}`,
|
|
73176
|
-
Accept: "application/vnd.github+json"
|
|
73177
|
-
}
|
|
73178
|
-
});
|
|
73179
|
-
if (!resp.ok)
|
|
73180
|
-
return null;
|
|
73181
|
-
const data = await resp.json();
|
|
73182
|
-
const sha = data.sha;
|
|
73183
|
-
return typeof sha === "string" ? sha : null;
|
|
73184
|
-
} catch {
|
|
73185
|
-
return null;
|
|
73186
|
-
} finally {
|
|
73187
|
-
clearTimeout(timeout);
|
|
73188
|
-
}
|
|
73189
|
-
}
|
|
73190
|
-
function displayNotice() {
|
|
73191
|
-
const msg = `
|
|
73192
|
-
${import_picocolors23.default.yellow(" Dosu skill update available")}
|
|
73193
|
-
` + `${import_picocolors23.default.dim(' Run "dosu skill update" to upgrade')}
|
|
73194
|
-
`;
|
|
73195
|
-
console.error(msg);
|
|
73196
|
-
}
|
|
73197
|
-
function checkForSkillUpdates() {
|
|
73198
|
-
try {
|
|
73199
|
-
const cache = readSkillCache();
|
|
73200
|
-
if (cache?.installedSha && cache.latestSha && cache.latestSha !== cache.installedSha) {
|
|
73201
|
-
displayNotice();
|
|
73202
|
-
}
|
|
73203
|
-
const isStale = !cache || Date.now() - cache.lastCheck > CHECK_INTERVAL_MS2;
|
|
73204
|
-
if (isStale) {
|
|
73205
|
-
fetchLatestSha().then((latest) => {
|
|
73206
|
-
const fresh = readSkillCache();
|
|
73207
|
-
writeSkillCache({
|
|
73208
|
-
lastCheck: Date.now(),
|
|
73209
|
-
latestSha: latest ?? fresh?.latestSha ?? "",
|
|
73210
|
-
installedSha: fresh?.installedSha ?? ""
|
|
73211
|
-
});
|
|
73212
|
-
if (latest) {
|
|
73213
|
-
logger.debug("skill-update-check", `Cached latest SHA: ${latest}`);
|
|
73214
|
-
}
|
|
73215
|
-
}).catch((err) => {
|
|
73216
|
-
logger.error("skill-update-check", `Background fetch failed: ${err}`);
|
|
73217
|
-
});
|
|
73218
|
-
}
|
|
73219
|
-
} catch (err) {
|
|
73220
|
-
logger.error("skill-update-check", `Skill update check failed: ${err}`);
|
|
73221
|
-
}
|
|
73222
|
-
}
|
|
73223
|
-
var import_picocolors23, CACHE_FILENAME3 = "skill-update-check.json", CHECK_INTERVAL_MS2, FETCH_TIMEOUT_MS2 = 5000, GITHUB_API_URL = "https://api.github.com/repos/dosu-ai/dosu-skill/commits/HEAD";
|
|
73224
|
-
var init_skill_update_check = __esm(() => {
|
|
73225
|
-
init_config();
|
|
73226
|
-
init_logger();
|
|
73227
|
-
import_picocolors23 = __toESM(require_picocolors(), 1);
|
|
73228
|
-
CHECK_INTERVAL_MS2 = 24 * 60 * 60 * 1000;
|
|
73229
|
-
});
|
|
73230
|
-
|
|
73231
|
-
// src/commands/skill.ts
|
|
73232
|
-
import { exec as exec2, execSync as execSync2 } from "node:child_process";
|
|
73233
|
-
import { homedir as homedir6 } from "node:os";
|
|
73234
|
-
import { join as join21 } from "node:path";
|
|
73235
|
-
function skillAgentIDsForProviders(providerIDs) {
|
|
73236
|
-
return [
|
|
73237
|
-
...new Set(providerIDs.map((providerID) => SKILL_AGENT_BY_PROVIDER[providerID]).filter((agent) => Boolean(agent)))
|
|
73238
|
-
];
|
|
73239
|
-
}
|
|
73240
|
-
function skillInstallTargetForProvider(providerID) {
|
|
73241
|
-
const agentID = SKILL_AGENT_BY_PROVIDER[providerID];
|
|
73242
|
-
if (!agentID)
|
|
73243
|
-
return null;
|
|
73244
|
-
if (agentID === "claude-code") {
|
|
73245
|
-
const claudeConfigDir = process.env.CLAUDE_CONFIG_DIR?.trim() || join21(homedir6(), ".claude");
|
|
73246
|
-
return { path: join21(claudeConfigDir, "skills", SKILL_NAME), symlink: true };
|
|
73247
|
-
}
|
|
73248
|
-
if (agentID === "windsurf") {
|
|
73249
|
-
return {
|
|
73250
|
-
path: join21(homedir6(), ".codeium", "windsurf", "skills", SKILL_NAME),
|
|
73251
|
-
symlink: true
|
|
73252
|
-
};
|
|
73253
|
-
}
|
|
73254
|
-
return {
|
|
73255
|
-
path: join21(homedir6(), ".agents", "skills", SKILL_NAME),
|
|
73256
|
-
symlink: false
|
|
73257
|
-
};
|
|
73258
|
-
}
|
|
73259
|
-
function skillAgentArgs(providerIDs) {
|
|
73260
|
-
const agents = providerIDs === undefined ? SUPPORTED_SKILL_AGENTS : skillAgentIDsForProviders(providerIDs);
|
|
73261
|
-
return agents.map((agent) => `-a ${agent}`).join(" ");
|
|
73262
|
-
}
|
|
73263
|
-
function execQuiet(command) {
|
|
73264
|
-
return new Promise((resolve6, reject) => {
|
|
73265
|
-
exec2(command, { windowsHide: true }, (error61) => {
|
|
73266
|
-
if (error61)
|
|
73267
|
-
reject(error61);
|
|
73268
|
-
else
|
|
73269
|
-
resolve6();
|
|
73270
|
-
});
|
|
73271
|
-
});
|
|
73272
|
-
}
|
|
73273
|
-
async function installSkill(providerIDs, options = {}) {
|
|
73274
|
-
const agentArgs = skillAgentArgs(providerIDs);
|
|
73275
|
-
if (!agentArgs) {
|
|
73276
|
-
logger.debug("skill", "No selected providers support the Dosu skill");
|
|
73277
|
-
return { success: true };
|
|
73278
|
-
}
|
|
73279
|
-
try {
|
|
73280
|
-
const command = `npx skills add ${SKILL_REPO} -g ${agentArgs} -s "*" -y`;
|
|
73281
|
-
if (options.quiet)
|
|
73282
|
-
await execQuiet(command);
|
|
73283
|
-
else
|
|
73284
|
-
execSync2(command, { stdio: "inherit" });
|
|
73285
|
-
} catch (err) {
|
|
73286
|
-
logger.error("skill", `Failed to install skill: ${err}`);
|
|
73287
|
-
return { success: false };
|
|
73288
|
-
}
|
|
73289
|
-
const sha = await fetchLatestSha();
|
|
73290
|
-
if (sha) {
|
|
73291
|
-
writeSkillCache({
|
|
73292
|
-
lastCheck: Date.now(),
|
|
73293
|
-
latestSha: sha,
|
|
73294
|
-
installedSha: sha
|
|
73295
|
-
});
|
|
73296
|
-
return { success: true, sha };
|
|
73297
|
-
}
|
|
73298
|
-
logger.debug("skill", "Skill installed but could not fetch latest SHA");
|
|
73299
|
-
return { success: true };
|
|
73300
|
-
}
|
|
73301
|
-
function installedSkillNames() {
|
|
73302
|
-
let entries;
|
|
73303
|
-
try {
|
|
73304
|
-
const json2 = execSync2("npx skills list -g --json", {
|
|
73305
|
-
encoding: "utf-8",
|
|
73306
|
-
stdio: ["ignore", "pipe", "ignore"]
|
|
73307
|
-
});
|
|
73308
|
-
const parsed = JSON.parse(json2);
|
|
73309
|
-
if (!Array.isArray(parsed))
|
|
73310
|
-
throw new Error("expected a JSON array");
|
|
73311
|
-
entries = parsed;
|
|
73312
|
-
} catch (err) {
|
|
73313
|
-
logger.debug("skill", `Could not list installed skills: ${err}`);
|
|
73314
|
-
return null;
|
|
73315
|
-
}
|
|
73316
|
-
const names = [];
|
|
73317
|
-
for (const entry of entries) {
|
|
73318
|
-
if (entry.source !== SKILL_REPO || typeof entry.name !== "string")
|
|
73319
|
-
continue;
|
|
73320
|
-
if (!SAFE_SKILL_NAME.test(entry.name)) {
|
|
73321
|
-
logger.warn("skill", `Skipping skill with an unsupported name: ${entry.name}`);
|
|
73322
|
-
continue;
|
|
73323
|
-
}
|
|
73324
|
-
names.push(entry.name);
|
|
73325
|
-
}
|
|
73326
|
-
return names;
|
|
73327
|
-
}
|
|
73328
|
-
function skillCommand() {
|
|
73329
|
-
const cmd = new Command("skill").description("Manage the Dosu agent skill");
|
|
73330
|
-
cmd.command("install").description("Install the Dosu skill for AI coding agents").action(async () => {
|
|
73331
|
-
console.log(`Installing skills from ${SKILL_REPO}...`);
|
|
73332
|
-
const result = await installSkill();
|
|
73333
|
-
if (result.success) {
|
|
73334
|
-
console.log(import_picocolors24.default.green(`
|
|
73335
|
-
✓ Skills installed successfully.`));
|
|
73336
|
-
} else {
|
|
73337
|
-
console.error(import_picocolors24.default.red(`
|
|
73338
|
-
Failed to install skill. Make sure npx is available.`));
|
|
73339
|
-
process.exit(1);
|
|
73340
|
-
}
|
|
73341
|
-
});
|
|
73342
|
-
cmd.command("remove").description("Remove the Dosu skills").action(() => {
|
|
73343
|
-
const installed = installedSkillNames();
|
|
73344
|
-
if (installed?.length === 0) {
|
|
73345
|
-
console.log(`No skills from ${SKILL_REPO} are installed.`);
|
|
73346
|
-
return;
|
|
73347
|
-
}
|
|
73348
|
-
const targets = installed ?? [SKILL_NAME];
|
|
73349
|
-
console.log(`Removing skills from ${SKILL_REPO}...`);
|
|
73350
|
-
try {
|
|
73351
|
-
execSync2(`npx skills remove -g ${targets.join(" ")} -y`, {
|
|
73352
|
-
stdio: "inherit"
|
|
73353
|
-
});
|
|
73354
|
-
clearInstalledSha();
|
|
73355
|
-
console.log(import_picocolors24.default.green(`
|
|
73356
|
-
✓ Skills removed.`));
|
|
73357
|
-
} catch {
|
|
73358
|
-
console.error(import_picocolors24.default.red(`
|
|
73359
|
-
Failed to remove skills.`));
|
|
73360
|
-
process.exit(1);
|
|
73361
|
-
}
|
|
73362
|
-
});
|
|
73363
|
-
cmd.command("update").description("Update the Dosu skill to the latest version").action(async () => {
|
|
73364
|
-
console.log(`Updating skills from ${SKILL_REPO}...`);
|
|
73365
|
-
const result = await installSkill();
|
|
73366
|
-
if (!result.success) {
|
|
73367
|
-
console.error(import_picocolors24.default.red(`
|
|
73368
|
-
Failed to update skill.`));
|
|
73369
|
-
process.exit(1);
|
|
73370
|
-
}
|
|
73371
|
-
console.log(import_picocolors24.default.green(`
|
|
73372
|
-
✓ Skills updated.`));
|
|
73373
|
-
});
|
|
73374
|
-
return cmd;
|
|
73375
|
-
}
|
|
73376
|
-
var import_picocolors24, SKILL_REPO = "dosu-ai/dosu-skill", SKILL_NAME = "dosu", SAFE_SKILL_NAME, SUPPORTED_SKILL_AGENTS, SKILL_AGENT_BY_PROVIDER;
|
|
73377
|
-
var init_skill = __esm(() => {
|
|
73378
|
-
init_commander();
|
|
73379
|
-
init_logger();
|
|
73380
|
-
init_skill_update_check();
|
|
73381
|
-
import_picocolors24 = __toESM(require_picocolors(), 1);
|
|
73382
|
-
SAFE_SKILL_NAME = /^[a-zA-Z0-9][a-zA-Z0-9._-]*$/;
|
|
73383
|
-
SUPPORTED_SKILL_AGENTS = [
|
|
73384
|
-
"claude-code",
|
|
73385
|
-
"cursor",
|
|
73386
|
-
"gemini-cli",
|
|
73387
|
-
"codex",
|
|
73388
|
-
"windsurf",
|
|
73389
|
-
"zed",
|
|
73390
|
-
"cline",
|
|
73391
|
-
"github-copilot",
|
|
73392
|
-
"opencode",
|
|
73393
|
-
"antigravity"
|
|
73394
|
-
];
|
|
73395
|
-
SKILL_AGENT_BY_PROVIDER = {
|
|
73396
|
-
claude: "claude-code",
|
|
73397
|
-
cursor: "cursor",
|
|
73398
|
-
vscode: "github-copilot",
|
|
73399
|
-
gemini: "gemini-cli",
|
|
73400
|
-
codex: "codex",
|
|
73401
|
-
windsurf: "windsurf",
|
|
73402
|
-
zed: "zed",
|
|
73403
|
-
cline: "cline",
|
|
73404
|
-
"cline-cli": "cline",
|
|
73405
|
-
copilot: "github-copilot",
|
|
73406
|
-
opencode: "opencode",
|
|
73407
|
-
antigravity: "antigravity"
|
|
73408
|
-
};
|
|
73409
|
-
});
|
|
73410
|
-
|
|
73411
|
-
// src/telemetry/settings.ts
|
|
73412
|
-
import { randomUUID as randomUUID3 } from "node:crypto";
|
|
73413
|
-
import {
|
|
73414
|
-
existsSync as existsSync18,
|
|
73415
|
-
lstatSync as lstatSync2,
|
|
73416
|
-
mkdirSync as mkdirSync11,
|
|
73417
|
-
readFileSync as readFileSync19,
|
|
73418
|
-
renameSync as renameSync5,
|
|
73419
|
-
unlinkSync as unlinkSync3,
|
|
73420
|
-
writeFileSync as writeFileSync12
|
|
73421
|
-
} from "node:fs";
|
|
73422
|
-
import { join as join22 } from "node:path";
|
|
73423
|
-
function getTelemetrySettingsPath() {
|
|
73424
|
-
return join22(getConfigDir(), TELEMETRY_SETTINGS_FILENAME);
|
|
73425
|
-
}
|
|
73426
|
-
function loadTelemetrySettings() {
|
|
73427
|
-
return readTelemetrySettings().settings;
|
|
73428
|
-
}
|
|
73429
|
-
function setTelemetryEnabled(enabled) {
|
|
73430
|
-
if (typeof enabled !== "boolean")
|
|
73431
|
-
return false;
|
|
73432
|
-
const current = readTelemetrySettings();
|
|
73433
|
-
if (current.status === "unsupported" || current.status === "error")
|
|
73434
|
-
return false;
|
|
73435
|
-
const settings2 = current.status === "current" ? current.settings : emptyTelemetrySettings();
|
|
73436
|
-
if (enabled)
|
|
73437
|
-
delete settings2.disabled;
|
|
73438
|
-
else
|
|
73439
|
-
settings2.disabled = true;
|
|
73440
|
-
return writeTelemetrySettings(settings2);
|
|
73441
|
-
}
|
|
73442
|
-
function telemetryDisabledByEnvironment() {
|
|
73443
|
-
if (isEnabledEnvironmentFlag(process.env.DO_NOT_TRACK))
|
|
73444
|
-
return "DO_NOT_TRACK";
|
|
73445
|
-
if (isEnabledEnvironmentFlag(process.env.DOSU_TELEMETRY_DISABLED)) {
|
|
73446
|
-
return "DOSU_TELEMETRY_DISABLED";
|
|
73447
|
-
}
|
|
73448
|
-
return;
|
|
73449
|
-
}
|
|
73450
|
-
function isTelemetryEnabled(settings2 = loadTelemetrySettings()) {
|
|
73451
|
-
if (telemetryDisabledByEnvironment() !== undefined)
|
|
73452
|
-
return false;
|
|
73453
|
-
return settings2.disabled !== true;
|
|
73454
|
-
}
|
|
73455
|
-
function getOrCreateInstallID() {
|
|
73456
|
-
const current = readTelemetrySettings();
|
|
73457
|
-
if (current.status === "current" && current.settings.install_id) {
|
|
73458
|
-
return current.settings.install_id;
|
|
73459
|
-
}
|
|
73460
|
-
if (current.status !== "current" && current.status !== "missing")
|
|
73461
|
-
return;
|
|
73462
|
-
const installID = randomUUID3();
|
|
73463
|
-
const settings2 = current.status === "current" ? current.settings : emptyTelemetrySettings();
|
|
73464
|
-
settings2.install_id = installID;
|
|
73465
|
-
return writeTelemetrySettings(settings2) ? installID : undefined;
|
|
73466
|
-
}
|
|
73467
|
-
function resetInstallID() {
|
|
73468
|
-
const current = readTelemetrySettings();
|
|
73469
|
-
if (current.status !== "current" && current.status !== "missing")
|
|
73470
|
-
return;
|
|
73471
|
-
const settings2 = current.status === "current" ? current.settings : emptyTelemetrySettings();
|
|
73472
|
-
const installID = randomUUID3();
|
|
73473
|
-
settings2.install_id = installID;
|
|
73474
|
-
return writeTelemetrySettings(settings2) ? installID : undefined;
|
|
73475
|
-
}
|
|
73476
|
-
function readTelemetrySettings() {
|
|
73477
|
-
const path3 = getTelemetrySettingsPath();
|
|
73478
|
-
let file2;
|
|
73479
|
-
try {
|
|
73480
|
-
file2 = lstatSync2(path3);
|
|
73481
|
-
} catch (error61) {
|
|
73482
|
-
if (isErrorCode(error61, "ENOENT")) {
|
|
73483
|
-
return { status: "missing", settings: emptyTelemetrySettings() };
|
|
73484
|
-
}
|
|
73485
|
-
return { status: "error", settings: disabledTelemetrySettings() };
|
|
73486
|
-
}
|
|
73487
|
-
if (!file2.isFile() || file2.size > MAX_TELEMETRY_SETTINGS_BYTES) {
|
|
73488
|
-
return { status: "invalid", settings: disabledTelemetrySettings() };
|
|
73489
|
-
}
|
|
73490
|
-
let content;
|
|
73491
|
-
try {
|
|
73492
|
-
content = readFileSync19(path3, "utf-8");
|
|
73493
|
-
} catch {
|
|
73494
|
-
return { status: "error", settings: disabledTelemetrySettings() };
|
|
73495
|
-
}
|
|
73496
|
-
let raw;
|
|
73497
|
-
try {
|
|
73498
|
-
raw = JSON.parse(content);
|
|
73499
|
-
} catch {
|
|
73500
|
-
return { status: "invalid", settings: disabledTelemetrySettings() };
|
|
73501
|
-
}
|
|
73502
|
-
if (!isRecord5(raw))
|
|
73503
|
-
return { status: "invalid", settings: disabledTelemetrySettings() };
|
|
73504
|
-
if (raw.schema_version !== TELEMETRY_SCHEMA_VERSION) {
|
|
73505
|
-
const status = "schema_version" in raw ? "unsupported" : "invalid";
|
|
73506
|
-
return { status, settings: disabledTelemetrySettings() };
|
|
73507
|
-
}
|
|
73508
|
-
if ("disabled" in raw && typeof raw.disabled !== "boolean") {
|
|
73509
|
-
return { status: "invalid", settings: disabledTelemetrySettings() };
|
|
73510
|
-
}
|
|
73511
|
-
const settings2 = emptyTelemetrySettings();
|
|
73512
|
-
if (raw.disabled === true)
|
|
73513
|
-
settings2.disabled = true;
|
|
73514
|
-
if (typeof raw.install_id === "string" && isUUID(raw.install_id)) {
|
|
73515
|
-
settings2.install_id = raw.install_id;
|
|
73516
|
-
}
|
|
73517
|
-
return { status: "current", settings: settings2 };
|
|
73518
|
-
}
|
|
73519
|
-
function writeTelemetrySettings(settings2) {
|
|
73520
|
-
const path3 = getTelemetrySettingsPath();
|
|
73521
|
-
const dir = getConfigDir();
|
|
73522
|
-
const temporaryPath = `${path3}.${process.pid}.${randomUUID3()}.tmp`;
|
|
73523
|
-
try {
|
|
73524
|
-
if (!existsSync18(dir))
|
|
73525
|
-
mkdirSync11(dir, { recursive: true, mode: 448 });
|
|
73526
|
-
writeFileSync12(temporaryPath, `${JSON.stringify(settings2, null, 2)}
|
|
73527
|
-
`, {
|
|
73528
|
-
flag: "wx",
|
|
73529
|
-
mode: 384
|
|
73530
|
-
});
|
|
73531
|
-
renameSync5(temporaryPath, path3);
|
|
73532
|
-
return true;
|
|
73533
|
-
} catch {
|
|
73534
|
-
try {
|
|
73535
|
-
unlinkSync3(temporaryPath);
|
|
73536
|
-
} catch {}
|
|
73537
|
-
return false;
|
|
73942
|
+
}
|
|
73943
|
+
logger.debug("learner", `run ${runID} threw: ${text}`);
|
|
73944
|
+
return {
|
|
73945
|
+
outcome: "error",
|
|
73946
|
+
notesWritten,
|
|
73947
|
+
turns,
|
|
73948
|
+
message: abort.signal.aborted ? "Study run timed out and was aborted." : "Study run failed; see debug log for details."
|
|
73949
|
+
};
|
|
73950
|
+
} finally {
|
|
73951
|
+
clearTimeout(timer);
|
|
73952
|
+
configDir.cleanup();
|
|
73538
73953
|
}
|
|
73539
73954
|
}
|
|
73540
|
-
|
|
73541
|
-
|
|
73542
|
-
|
|
73543
|
-
|
|
73544
|
-
|
|
73545
|
-
|
|
73546
|
-
|
|
73547
|
-
|
|
73548
|
-
|
|
73549
|
-
|
|
73550
|
-
|
|
73551
|
-
|
|
73552
|
-
|
|
73553
|
-
|
|
73554
|
-
|
|
73555
|
-
|
|
73556
|
-
|
|
73557
|
-
|
|
73558
|
-
|
|
73559
|
-
}
|
|
73560
|
-
|
|
73561
|
-
|
|
73562
|
-
|
|
73563
|
-
|
|
73564
|
-
|
|
73565
|
-
init_config();
|
|
73566
|
-
MAX_TELEMETRY_SETTINGS_BYTES = 16 * 1024;
|
|
73955
|
+
var DEFAULT_MAX_TURNS = 160, DEFAULT_MAX_NOTES = 80, DEFAULT_TIMEOUT_MS2, KNOWLEDGE_SERVER_NAME = "dosu", GATEWAY_ERRORS, TRACE_SNIPPET_LIMIT = 400;
|
|
73956
|
+
var init_runner = __esm(() => {
|
|
73957
|
+
init_logger();
|
|
73958
|
+
init_config_helpers();
|
|
73959
|
+
init_notes();
|
|
73960
|
+
init_config_dir();
|
|
73961
|
+
init_conflicts();
|
|
73962
|
+
init_env();
|
|
73963
|
+
init_executable();
|
|
73964
|
+
init_tools();
|
|
73965
|
+
DEFAULT_TIMEOUT_MS2 = 30 * 60 * 1000;
|
|
73966
|
+
GATEWAY_ERRORS = {
|
|
73967
|
+
dosu_consent_off: {
|
|
73968
|
+
outcome: "consent_off",
|
|
73969
|
+
message: "Your org hasn't enabled Dosu Remote Sessions. Ask an org admin to turn it on."
|
|
73970
|
+
},
|
|
73971
|
+
dosu_credit_limit_reached: {
|
|
73972
|
+
outcome: "credit_limit",
|
|
73973
|
+
message: "Your org has used its Dosu credits for this billing period. An admin can enable overage or upgrade."
|
|
73974
|
+
},
|
|
73975
|
+
dosu_quota_exceeded: {
|
|
73976
|
+
outcome: "quota_exceeded",
|
|
73977
|
+
message: "Daily Dosu Remote Sessions budget reached; runs resume tomorrow."
|
|
73978
|
+
}
|
|
73979
|
+
};
|
|
73567
73980
|
});
|
|
73568
73981
|
|
|
73569
|
-
// src/
|
|
73570
|
-
|
|
73571
|
-
|
|
73572
|
-
|
|
73573
|
-
|
|
73574
|
-
import { existsSync as existsSync19 } from "node:fs";
|
|
73575
|
-
import { request as httpRequest2 } from "node:http";
|
|
73576
|
-
import { request as httpsRequest } from "node:https";
|
|
73577
|
-
import { isAbsolute as isAbsolute4, relative as relative2, resolve as resolve6, sep as sep5 } from "node:path";
|
|
73578
|
-
import { fileURLToPath as fileURLToPath2 } from "node:url";
|
|
73579
|
-
function safeString(value, pattern, fallback) {
|
|
73580
|
-
if (typeof value !== "string" || !pattern.test(value))
|
|
73581
|
-
return fallback;
|
|
73582
|
-
return value;
|
|
73583
|
-
}
|
|
73584
|
-
function canonicalCommand(value) {
|
|
73585
|
-
if (typeof value !== "string")
|
|
73586
|
-
return UNKNOWN_COMMAND;
|
|
73587
|
-
const command = value.trim();
|
|
73588
|
-
if (command.length > 80)
|
|
73589
|
-
return UNKNOWN_COMMAND;
|
|
73590
|
-
if (!/^[a-z][a-z0-9-]*(?: [a-z][a-z0-9-]*){0,3}$/.test(command)) {
|
|
73591
|
-
return UNKNOWN_COMMAND;
|
|
73592
|
-
}
|
|
73593
|
-
return command;
|
|
73594
|
-
}
|
|
73595
|
-
function normalizeContext(context) {
|
|
73596
|
-
const isAuthenticated2 = context?.isAuthenticated === true;
|
|
73597
|
-
const id2 = safeRead(context?.user, "id");
|
|
73598
|
-
const email3 = safeRead(context?.user, "email");
|
|
73599
|
-
const orgId = safeRead(context, "orgId");
|
|
73600
|
-
const user = isAuthenticated2 && isUUID2(id2) ? { id: id2, ...validEmail(email3) ? { email: email3 } : {} } : undefined;
|
|
73601
|
-
return {
|
|
73602
|
-
mode: context?.mode === "oss" ? "oss" : "cloud",
|
|
73603
|
-
isAuthenticated: isAuthenticated2,
|
|
73604
|
-
...user ? { user } : {},
|
|
73605
|
-
...user && isUUID2(orgId) ? { orgId } : {}
|
|
73606
|
-
};
|
|
73607
|
-
}
|
|
73608
|
-
function normalizeExitCode(value, fallback = 1) {
|
|
73609
|
-
if (typeof value !== "number" || !Number.isFinite(value))
|
|
73610
|
-
return fallback;
|
|
73611
|
-
return Math.min(255, Math.max(0, Math.trunc(value)));
|
|
73612
|
-
}
|
|
73613
|
-
function normalizeRuntime(runtime) {
|
|
73614
|
-
return {
|
|
73615
|
-
version: safeString(runtime.version, /^[A-Za-z0-9.+_-]{1,32}$/, "unknown"),
|
|
73616
|
-
installChannel: safeString(runtime.installChannel, /^[A-Za-z][A-Za-z0-9_-]{0,31}$/, "unknown"),
|
|
73617
|
-
platform: safeString(runtime.platform, /^[A-Za-z0-9_-]{1,24}$/, "unknown"),
|
|
73618
|
-
arch: safeString(runtime.arch, /^[A-Za-z0-9_-]{1,24}$/, "unknown"),
|
|
73619
|
-
runtime: safeString(runtime.runtime, /^[A-Za-z][A-Za-z0-9_-]{0,23}$/, "unknown"),
|
|
73620
|
-
runtimeMajor: Math.min(9999, Math.max(0, Math.trunc(runtime.runtimeMajor) || 0)),
|
|
73621
|
-
isCi: runtime.isCi === true,
|
|
73622
|
-
isTty: runtime.isTty === true
|
|
73623
|
-
};
|
|
73624
|
-
}
|
|
73625
|
-
function isUUID2(value) {
|
|
73626
|
-
return typeof value === "string" && /^[0-9a-f]{8}-[0-9a-f]{4}-[1-8][0-9a-f]{3}-[89ab][0-9a-f]{3}-[0-9a-f]{12}$/i.test(value);
|
|
73627
|
-
}
|
|
73628
|
-
function validEmail(value) {
|
|
73629
|
-
return typeof value === "string" && value.length <= 254 && /^[A-Za-z0-9.!#$%&'*+/=?^_`{|}~-]+@[A-Za-z0-9](?:[A-Za-z0-9-]{0,61}[A-Za-z0-9])?(?:\.[A-Za-z0-9](?:[A-Za-z0-9-]{0,61}[A-Za-z0-9])?)+$/.test(value);
|
|
73630
|
-
}
|
|
73631
|
-
function validEventId(value) {
|
|
73632
|
-
return typeof value === "string" && /^[0-9a-f]{32}$/i.test(value);
|
|
73633
|
-
}
|
|
73634
|
-
function validDebugId(value) {
|
|
73635
|
-
return typeof value === "string" && /^[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}$/i.test(value);
|
|
73636
|
-
}
|
|
73637
|
-
function validErrorType(value) {
|
|
73638
|
-
return typeof value === "string" && SAFE_ERROR_TYPES.has(value);
|
|
73639
|
-
}
|
|
73640
|
-
function validErrorCode(value) {
|
|
73641
|
-
return typeof value === "string" && SAFE_ERROR_CODES.has(value);
|
|
73642
|
-
}
|
|
73643
|
-
function validStatus(value) {
|
|
73644
|
-
return typeof value === "number" && Number.isInteger(value) && value >= 100 && value <= 599;
|
|
73645
|
-
}
|
|
73646
|
-
function objectLike(value) {
|
|
73647
|
-
return typeof value === "object" && value !== null || typeof value === "function";
|
|
73982
|
+
// src/version/skill-update-check.ts
|
|
73983
|
+
import { existsSync as existsSync18, mkdirSync as mkdirSync10, readFileSync as readFileSync18, writeFileSync as writeFileSync11 } from "node:fs";
|
|
73984
|
+
import { join as join20 } from "node:path";
|
|
73985
|
+
function getCachePath2() {
|
|
73986
|
+
return join20(getConfigDir(), CACHE_FILENAME3);
|
|
73648
73987
|
}
|
|
73649
|
-
function
|
|
73650
|
-
if (!objectLike(value))
|
|
73651
|
-
return;
|
|
73988
|
+
function readSkillCache() {
|
|
73652
73989
|
try {
|
|
73653
|
-
|
|
73654
|
-
|
|
73655
|
-
|
|
73656
|
-
|
|
73657
|
-
|
|
73658
|
-
|
|
73659
|
-
if (typeof stack !== "string" || !CLI_PACKAGE_ROOT)
|
|
73660
|
-
return [];
|
|
73661
|
-
const frames = [];
|
|
73662
|
-
const lines = stack.slice(0, 1e5).split(`
|
|
73663
|
-
`, 200);
|
|
73664
|
-
for (const originalLine of lines) {
|
|
73665
|
-
if (frames.length >= MAX_STACK_FRAMES)
|
|
73666
|
-
break;
|
|
73667
|
-
const parsed = parseOwnedStackLine(originalLine, CLI_PACKAGE_ROOT);
|
|
73668
|
-
if (!parsed)
|
|
73669
|
-
continue;
|
|
73670
|
-
const { filename, lineno, colno } = parsed;
|
|
73671
|
-
if (!Number.isSafeInteger(lineno) || !Number.isSafeInteger(colno) || lineno < 1 || colno < 1 || lineno > 1e7 || colno > 1e7) {
|
|
73672
|
-
continue;
|
|
73990
|
+
const path3 = getCachePath2();
|
|
73991
|
+
if (!existsSync18(path3))
|
|
73992
|
+
return null;
|
|
73993
|
+
const data = JSON.parse(readFileSync18(path3, "utf-8"));
|
|
73994
|
+
if (typeof data.lastCheck === "number" && typeof data.latestSha === "string" && typeof data.installedSha === "string") {
|
|
73995
|
+
return data;
|
|
73673
73996
|
}
|
|
73674
|
-
|
|
73997
|
+
return null;
|
|
73998
|
+
} catch {
|
|
73999
|
+
return null;
|
|
73675
74000
|
}
|
|
73676
|
-
return frames;
|
|
73677
74001
|
}
|
|
73678
|
-
function
|
|
74002
|
+
function writeSkillCache(cache) {
|
|
73679
74003
|
try {
|
|
73680
|
-
const
|
|
73681
|
-
|
|
73682
|
-
|
|
73683
|
-
return modulePath.slice(0, -suffix.length);
|
|
74004
|
+
const dir = getConfigDir();
|
|
74005
|
+
if (!existsSync18(dir)) {
|
|
74006
|
+
mkdirSync10(dir, { recursive: true, mode: 448 });
|
|
73684
74007
|
}
|
|
74008
|
+
writeFileSync11(getCachePath2(), JSON.stringify(cache), { mode: 384 });
|
|
73685
74009
|
} catch {}
|
|
73686
|
-
return;
|
|
73687
74010
|
}
|
|
73688
|
-
function
|
|
73689
|
-
const
|
|
73690
|
-
|
|
73691
|
-
|
|
73692
|
-
|
|
73693
|
-
|
|
73694
|
-
|
|
73695
|
-
|
|
73696
|
-
absolutePath = fileURLToPath2(absolutePath);
|
|
73697
|
-
} catch {
|
|
73698
|
-
return;
|
|
73699
|
-
}
|
|
73700
|
-
if (!isAbsolute4(absolutePath))
|
|
73701
|
-
return;
|
|
73702
|
-
const resolvedPath = resolve6(absolutePath);
|
|
73703
|
-
const relativePath = relative2(packageRoot, resolvedPath);
|
|
73704
|
-
if (relativePath === "" || relativePath === ".." || relativePath.startsWith(`..${sep5}`) || isAbsolute4(relativePath)) {
|
|
73705
|
-
return;
|
|
73706
|
-
}
|
|
73707
|
-
const filename = relativePath.replaceAll("\\", "/");
|
|
73708
|
-
if (!/^(?:src\/(?:[A-Za-z0-9_.-]+\/)*[A-Za-z0-9_.-]+\.(?:[cm]?[jt]sx?)|bin\/dosu\.js)$/.test(filename)) {
|
|
73709
|
-
return;
|
|
73710
|
-
}
|
|
74011
|
+
function clearInstalledSha() {
|
|
74012
|
+
const cache = readSkillCache();
|
|
74013
|
+
if (cache)
|
|
74014
|
+
writeSkillCache({ ...cache, installedSha: "" });
|
|
74015
|
+
}
|
|
74016
|
+
async function fetchLatestSha() {
|
|
74017
|
+
const controller = new AbortController;
|
|
74018
|
+
const timeout = setTimeout(() => controller.abort(), FETCH_TIMEOUT_MS2);
|
|
73711
74019
|
try {
|
|
73712
|
-
|
|
73713
|
-
|
|
74020
|
+
const resp = await fetch(GITHUB_API_URL, {
|
|
74021
|
+
signal: controller.signal,
|
|
74022
|
+
headers: {
|
|
74023
|
+
"User-Agent": `dosu-cli/${VERSION}`,
|
|
74024
|
+
Accept: "application/vnd.github+json"
|
|
74025
|
+
}
|
|
74026
|
+
});
|
|
74027
|
+
if (!resp.ok)
|
|
74028
|
+
return null;
|
|
74029
|
+
const data = await resp.json();
|
|
74030
|
+
const sha = data.sha;
|
|
74031
|
+
return typeof sha === "string" ? sha : null;
|
|
73714
74032
|
} catch {
|
|
73715
|
-
return;
|
|
74033
|
+
return null;
|
|
74034
|
+
} finally {
|
|
74035
|
+
clearTimeout(timeout);
|
|
73716
74036
|
}
|
|
73717
|
-
return { filename, lineno: Number(match[2]), colno: Number(match[3]) };
|
|
73718
74037
|
}
|
|
73719
|
-
function
|
|
73720
|
-
const
|
|
73721
|
-
|
|
73722
|
-
|
|
73723
|
-
|
|
73724
|
-
|
|
73725
|
-
safeRead(data, "httpStatus"),
|
|
73726
|
-
safeRead(normalizedError, "status"),
|
|
73727
|
-
safeRead(normalizedError, "statusCode")
|
|
73728
|
-
];
|
|
73729
|
-
const exitCodeCandidate = safeRead(normalizedError, "exitCode");
|
|
73730
|
-
const code = codeCandidates.find(validErrorCode);
|
|
73731
|
-
const status = statusCandidates.find(validStatus);
|
|
73732
|
-
const exitCode = typeof exitCodeCandidate === "number" && Number.isFinite(exitCodeCandidate) ? normalizeExitCode(exitCodeCandidate) : undefined;
|
|
73733
|
-
return {
|
|
73734
|
-
type: validErrorType(typeCandidate) ? typeCandidate : "Error",
|
|
73735
|
-
...code ? { code } : {},
|
|
73736
|
-
...status ? { status } : {},
|
|
73737
|
-
frames: parseStack(safeRead(normalizedError, "stack")),
|
|
73738
|
-
...exitCode === undefined ? {} : { exitCode }
|
|
73739
|
-
};
|
|
74038
|
+
function displayNotice() {
|
|
74039
|
+
const msg = `
|
|
74040
|
+
${import_picocolors23.default.yellow(" Dosu skill update available")}
|
|
74041
|
+
` + `${import_picocolors23.default.dim(' Run "dosu skill update" to upgrade')}
|
|
74042
|
+
`;
|
|
74043
|
+
console.error(msg);
|
|
73740
74044
|
}
|
|
73741
|
-
function
|
|
73742
|
-
|
|
73743
|
-
|
|
73744
|
-
|
|
73745
|
-
|
|
73746
|
-
return;
|
|
73747
|
-
seen.add(current);
|
|
73748
|
-
const code = safeRead(current, "code");
|
|
73749
|
-
if (code === "SESSION_EXPIRED" || code === "SESSION_PERSISTENCE_ERROR" || code === "SESSION_REFRESH_ERROR") {
|
|
73750
|
-
return current;
|
|
74045
|
+
function checkForSkillUpdates() {
|
|
74046
|
+
try {
|
|
74047
|
+
const cache = readSkillCache();
|
|
74048
|
+
if (cache?.installedSha && cache.latestSha && cache.latestSha !== cache.installedSha) {
|
|
74049
|
+
displayNotice();
|
|
73751
74050
|
}
|
|
73752
|
-
|
|
74051
|
+
const isStale = !cache || Date.now() - cache.lastCheck > CHECK_INTERVAL_MS2;
|
|
74052
|
+
if (isStale) {
|
|
74053
|
+
fetchLatestSha().then((latest) => {
|
|
74054
|
+
const fresh = readSkillCache();
|
|
74055
|
+
writeSkillCache({
|
|
74056
|
+
lastCheck: Date.now(),
|
|
74057
|
+
latestSha: latest ?? fresh?.latestSha ?? "",
|
|
74058
|
+
installedSha: fresh?.installedSha ?? ""
|
|
74059
|
+
});
|
|
74060
|
+
if (latest) {
|
|
74061
|
+
logger.debug("skill-update-check", `Cached latest SHA: ${latest}`);
|
|
74062
|
+
}
|
|
74063
|
+
}).catch((err) => {
|
|
74064
|
+
logger.error("skill-update-check", `Background fetch failed: ${err}`);
|
|
74065
|
+
});
|
|
74066
|
+
}
|
|
74067
|
+
} catch (err) {
|
|
74068
|
+
logger.error("skill-update-check", `Skill update check failed: ${err}`);
|
|
73753
74069
|
}
|
|
73754
|
-
return;
|
|
73755
74070
|
}
|
|
73756
|
-
|
|
73757
|
-
|
|
73758
|
-
|
|
73759
|
-
|
|
73760
|
-
|
|
73761
|
-
|
|
73762
|
-
|
|
73763
|
-
|
|
73764
|
-
|
|
73765
|
-
|
|
73766
|
-
|
|
73767
|
-
|
|
73768
|
-
|
|
74071
|
+
var import_picocolors23, CACHE_FILENAME3 = "skill-update-check.json", CHECK_INTERVAL_MS2, FETCH_TIMEOUT_MS2 = 5000, GITHUB_API_URL = "https://api.github.com/repos/dosu-ai/dosu-skill/commits/HEAD";
|
|
74072
|
+
var init_skill_update_check = __esm(() => {
|
|
74073
|
+
init_config();
|
|
74074
|
+
init_logger();
|
|
74075
|
+
import_picocolors23 = __toESM(require_picocolors(), 1);
|
|
74076
|
+
CHECK_INTERVAL_MS2 = 24 * 60 * 60 * 1000;
|
|
74077
|
+
});
|
|
74078
|
+
|
|
74079
|
+
// src/commands/skill.ts
|
|
74080
|
+
import { exec as exec2, execSync as execSync2 } from "node:child_process";
|
|
74081
|
+
import { homedir as homedir6 } from "node:os";
|
|
74082
|
+
import { join as join21 } from "node:path";
|
|
74083
|
+
function skillAgentIDsForProviders(providerIDs) {
|
|
74084
|
+
return [
|
|
74085
|
+
...new Set(providerIDs.map((providerID) => SKILL_AGENT_BY_PROVIDER[providerID]).filter((agent) => Boolean(agent)))
|
|
74086
|
+
];
|
|
73769
74087
|
}
|
|
73770
|
-
function
|
|
73771
|
-
const
|
|
73772
|
-
|
|
73773
|
-
|
|
73774
|
-
|
|
74088
|
+
function skillInstallTargetForProvider(providerID) {
|
|
74089
|
+
const agentID = SKILL_AGENT_BY_PROVIDER[providerID];
|
|
74090
|
+
if (!agentID)
|
|
74091
|
+
return null;
|
|
74092
|
+
if (agentID === "claude-code") {
|
|
74093
|
+
const claudeConfigDir = process.env.CLAUDE_CONFIG_DIR?.trim() || join21(homedir6(), ".claude");
|
|
74094
|
+
return { path: join21(claudeConfigDir, "skills", SKILL_NAME), symlink: true };
|
|
74095
|
+
}
|
|
74096
|
+
if (agentID === "windsurf") {
|
|
74097
|
+
return {
|
|
74098
|
+
path: join21(homedir6(), ".codeium", "windsurf", "skills", SKILL_NAME),
|
|
74099
|
+
symlink: true
|
|
74100
|
+
};
|
|
74101
|
+
}
|
|
73775
74102
|
return {
|
|
73776
|
-
|
|
73777
|
-
|
|
73778
|
-
event: "cli_command_completed",
|
|
73779
|
-
properties: {
|
|
73780
|
-
$geoip_disable: true,
|
|
73781
|
-
...context.user ? {} : { $process_person_profile: false },
|
|
73782
|
-
schema_version: 1,
|
|
73783
|
-
command: canonicalCommand(input2.command),
|
|
73784
|
-
result,
|
|
73785
|
-
duration_bucket: durationBucket(input2.durationMs),
|
|
73786
|
-
cli_version: runtime.version,
|
|
73787
|
-
install_channel: runtime.installChannel,
|
|
73788
|
-
platform: runtime.platform,
|
|
73789
|
-
arch: runtime.arch,
|
|
73790
|
-
runtime: runtime.runtime,
|
|
73791
|
-
runtime_major: runtime.runtimeMajor,
|
|
73792
|
-
is_ci: runtime.isCi,
|
|
73793
|
-
is_tty: runtime.isTty,
|
|
73794
|
-
mode: context.mode,
|
|
73795
|
-
...context.orgId ? {
|
|
73796
|
-
org_id: context.orgId,
|
|
73797
|
-
$groups: { organization: context.orgId }
|
|
73798
|
-
} : {},
|
|
73799
|
-
is_authenticated: context.isAuthenticated,
|
|
73800
|
-
exit_code: normalizeExitCode(input2.exitCode),
|
|
73801
|
-
...errorCode ? { error_code: errorCode } : {}
|
|
73802
|
-
}
|
|
74103
|
+
path: join21(homedir6(), ".agents", "skills", SKILL_NAME),
|
|
74104
|
+
symlink: false
|
|
73803
74105
|
};
|
|
73804
74106
|
}
|
|
73805
|
-
function
|
|
73806
|
-
|
|
73807
|
-
|
|
73808
|
-
const token = value.trim();
|
|
73809
|
-
return /^phc_[A-Za-z0-9_-]{3,252}$/.test(token) ? token : null;
|
|
74107
|
+
function skillAgentArgs(providerIDs) {
|
|
74108
|
+
const agents = providerIDs === undefined ? SUPPORTED_SKILL_AGENTS : skillAgentIDsForProviders(providerIDs);
|
|
74109
|
+
return agents.map((agent) => `-a ${agent}`).join(" ");
|
|
73810
74110
|
}
|
|
73811
|
-
function
|
|
73812
|
-
|
|
73813
|
-
|
|
74111
|
+
function execQuiet(command) {
|
|
74112
|
+
return new Promise((resolve7, reject) => {
|
|
74113
|
+
exec2(command, { windowsHide: true }, (error61) => {
|
|
74114
|
+
if (error61)
|
|
74115
|
+
reject(error61);
|
|
74116
|
+
else
|
|
74117
|
+
resolve7();
|
|
74118
|
+
});
|
|
74119
|
+
});
|
|
74120
|
+
}
|
|
74121
|
+
async function installSkill(providerIDs, options = {}) {
|
|
74122
|
+
const agentArgs = skillAgentArgs(providerIDs);
|
|
74123
|
+
if (!agentArgs) {
|
|
74124
|
+
logger.debug("skill", "No selected providers support the Dosu skill");
|
|
74125
|
+
return { success: true };
|
|
74126
|
+
}
|
|
73814
74127
|
try {
|
|
73815
|
-
const
|
|
73816
|
-
if (
|
|
73817
|
-
|
|
73818
|
-
|
|
73819
|
-
|
|
73820
|
-
|
|
73821
|
-
|
|
73822
|
-
|
|
73823
|
-
|
|
73824
|
-
|
|
73825
|
-
|
|
74128
|
+
const command = `npx skills add ${SKILL_REPO} -g ${agentArgs} -s "*" -y`;
|
|
74129
|
+
if (options.quiet)
|
|
74130
|
+
await execQuiet(command);
|
|
74131
|
+
else
|
|
74132
|
+
execSync2(command, { stdio: "inherit" });
|
|
74133
|
+
} catch (err) {
|
|
74134
|
+
logger.error("skill", `Failed to install skill: ${err}`);
|
|
74135
|
+
return { success: false };
|
|
74136
|
+
}
|
|
74137
|
+
const sha = await fetchLatestSha();
|
|
74138
|
+
if (sha) {
|
|
74139
|
+
writeSkillCache({
|
|
74140
|
+
lastCheck: Date.now(),
|
|
74141
|
+
latestSha: sha,
|
|
74142
|
+
installedSha: sha
|
|
74143
|
+
});
|
|
74144
|
+
return { success: true, sha };
|
|
73826
74145
|
}
|
|
74146
|
+
logger.debug("skill", "Skill installed but could not fetch latest SHA");
|
|
74147
|
+
return { success: true };
|
|
73827
74148
|
}
|
|
73828
|
-
function
|
|
73829
|
-
|
|
73830
|
-
return null;
|
|
73831
|
-
const dsn = value.trim();
|
|
74149
|
+
function installedSkillNames() {
|
|
74150
|
+
let entries;
|
|
73832
74151
|
try {
|
|
73833
|
-
const
|
|
73834
|
-
|
|
73835
|
-
|
|
73836
|
-
}
|
|
73837
|
-
const
|
|
73838
|
-
|
|
73839
|
-
|
|
73840
|
-
|
|
73841
|
-
|
|
73842
|
-
|
|
73843
|
-
const prefix = segments.length > 0 ? `/${segments.join("/")}` : "";
|
|
73844
|
-
return {
|
|
73845
|
-
dsn,
|
|
73846
|
-
endpoint: `${url2.protocol}//${url2.host}${prefix}/api/${projectId}/envelope/`,
|
|
73847
|
-
projectId,
|
|
73848
|
-
publicKey: url2.username
|
|
73849
|
-
};
|
|
73850
|
-
} catch {
|
|
74152
|
+
const json2 = execSync2("npx skills list -g --json", {
|
|
74153
|
+
encoding: "utf-8",
|
|
74154
|
+
stdio: ["ignore", "pipe", "ignore"]
|
|
74155
|
+
});
|
|
74156
|
+
const parsed = JSON.parse(json2);
|
|
74157
|
+
if (!Array.isArray(parsed))
|
|
74158
|
+
throw new Error("expected a JSON array");
|
|
74159
|
+
entries = parsed;
|
|
74160
|
+
} catch (err) {
|
|
74161
|
+
logger.debug("skill", `Could not list installed skills: ${err}`);
|
|
73851
74162
|
return null;
|
|
73852
74163
|
}
|
|
74164
|
+
const names = [];
|
|
74165
|
+
for (const entry of entries) {
|
|
74166
|
+
if (entry.source !== SKILL_REPO || typeof entry.name !== "string")
|
|
74167
|
+
continue;
|
|
74168
|
+
if (!SAFE_SKILL_NAME.test(entry.name)) {
|
|
74169
|
+
logger.warn("skill", `Skipping skill with an unsupported name: ${entry.name}`);
|
|
74170
|
+
continue;
|
|
74171
|
+
}
|
|
74172
|
+
names.push(entry.name);
|
|
74173
|
+
}
|
|
74174
|
+
return names;
|
|
73853
74175
|
}
|
|
73854
|
-
function
|
|
73855
|
-
|
|
73856
|
-
|
|
73857
|
-
|
|
73858
|
-
|
|
73859
|
-
|
|
73860
|
-
|
|
73861
|
-
|
|
73862
|
-
|
|
73863
|
-
|
|
73864
|
-
|
|
73865
|
-
|
|
73866
|
-
|
|
73867
|
-
|
|
73868
|
-
|
|
73869
|
-
|
|
73870
|
-
|
|
73871
|
-
|
|
73872
|
-
|
|
73873
|
-
function buildSentryEnvelope(input2) {
|
|
73874
|
-
const parsedDsn = parseSentryDsn(input2.dsn);
|
|
73875
|
-
if (!parsedDsn || !validEventId(input2.eventId))
|
|
73876
|
-
return null;
|
|
73877
|
-
const context = normalizeContext(input2.context);
|
|
73878
|
-
const runtime = normalizeRuntime(input2.runtime);
|
|
73879
|
-
const command = canonicalCommand(input2.command);
|
|
73880
|
-
const debugId = validDebugId(input2.debugId) ? input2.debugId.toLowerCase() : undefined;
|
|
73881
|
-
const error61 = {
|
|
73882
|
-
type: validErrorType(input2.error.type) ? input2.error.type : "Error",
|
|
73883
|
-
...validErrorCode(input2.error.code) ? { code: input2.error.code } : {},
|
|
73884
|
-
...validStatus(input2.error.status) ? { status: input2.error.status } : {},
|
|
73885
|
-
...typeof input2.error.exitCode === "number" && Number.isFinite(input2.error.exitCode) ? { exitCode: normalizeExitCode(input2.error.exitCode) } : {},
|
|
73886
|
-
frames: input2.error.frames.slice(0, MAX_STACK_FRAMES).map((frame) => {
|
|
73887
|
-
const filename = /^(?:src\/(?:[A-Za-z0-9_.-]+\/)*[A-Za-z0-9_.-]+\.(?:[cm]?[jt]sx?)|bin\/dosu\.js)$/.test(frame.filename) ? frame.filename : "src/unknown.ts";
|
|
73888
|
-
return {
|
|
73889
|
-
filename,
|
|
73890
|
-
...debugId && filename === "bin/dosu.js" ? { abs_path: BUNDLE_CODE_FILE } : {},
|
|
73891
|
-
lineno: Math.min(1e7, Math.max(1, Math.trunc(frame.lineno) || 1)),
|
|
73892
|
-
colno: Math.min(1e7, Math.max(1, Math.trunc(frame.colno) || 1)),
|
|
73893
|
-
in_app: true
|
|
73894
|
-
};
|
|
73895
|
-
}).reverse()
|
|
73896
|
-
};
|
|
73897
|
-
const exceptionValue = {
|
|
73898
|
-
type: error61.type,
|
|
73899
|
-
value: error61.code ?? error61.type,
|
|
73900
|
-
...error61.frames.length > 0 ? { stacktrace: { frames: error61.frames } } : {}
|
|
73901
|
-
};
|
|
73902
|
-
const newestFrame = error61.frames.at(-1);
|
|
73903
|
-
const safeCallsite = newestFrame ? `${newestFrame.filename}:${newestFrame.lineno}` : "unknown";
|
|
73904
|
-
const timestampMs = Number.isFinite(input2.timestampMs) ? Math.max(0, input2.timestampMs) : 0;
|
|
73905
|
-
const mappedBundle = debugId && error61.frames.some((frame) => frame.abs_path === BUNDLE_CODE_FILE);
|
|
73906
|
-
const event = {
|
|
73907
|
-
event_id: input2.eventId.toLowerCase(),
|
|
73908
|
-
timestamp: timestampMs / 1000,
|
|
73909
|
-
platform: "node",
|
|
73910
|
-
level: "error",
|
|
73911
|
-
release: `dosu-cli@${runtime.version}`,
|
|
73912
|
-
tags: sentryTags(command, context, runtime, error61),
|
|
73913
|
-
...context.user ? {
|
|
73914
|
-
user: {
|
|
73915
|
-
id: context.user.id,
|
|
73916
|
-
...context.user.email ? { email: context.user.email } : {}
|
|
73917
|
-
}
|
|
73918
|
-
} : {},
|
|
73919
|
-
...mappedBundle ? {
|
|
73920
|
-
debug_meta: {
|
|
73921
|
-
images: [{ type: "sourcemap", code_file: BUNDLE_CODE_FILE, debug_id: debugId }]
|
|
73922
|
-
}
|
|
73923
|
-
} : {},
|
|
73924
|
-
fingerprint: ["dosu-cli", command, error61.type, error61.code ?? "unknown", safeCallsite],
|
|
73925
|
-
exception: { values: [exceptionValue] }
|
|
73926
|
-
};
|
|
73927
|
-
const envelopeHeader = {
|
|
73928
|
-
event_id: input2.eventId.toLowerCase(),
|
|
73929
|
-
dsn: parsedDsn.dsn,
|
|
73930
|
-
sent_at: new Date(timestampMs).toISOString()
|
|
73931
|
-
};
|
|
73932
|
-
return {
|
|
73933
|
-
endpoint: parsedDsn.endpoint,
|
|
73934
|
-
body: `${JSON.stringify(envelopeHeader)}
|
|
73935
|
-
${JSON.stringify({ type: "event" })}
|
|
73936
|
-
${JSON.stringify(event)}`
|
|
73937
|
-
};
|
|
73938
|
-
}
|
|
73939
|
-
function fetchWithoutRedirect(url2, init = {}) {
|
|
73940
|
-
let parsedURL;
|
|
73941
|
-
try {
|
|
73942
|
-
parsedURL = new URL(url2);
|
|
73943
|
-
if (parsedURL.protocol !== "http:" && parsedURL.protocol !== "https:") {
|
|
73944
|
-
return Promise.reject(new Error("unsupported telemetry protocol"));
|
|
74176
|
+
function skillCommand() {
|
|
74177
|
+
const cmd = new Command("skill").description("Manage the Dosu agent skill");
|
|
74178
|
+
cmd.command("install").description("Install the Dosu skill for AI coding agents").action(async () => {
|
|
74179
|
+
console.log(`Installing skills from ${SKILL_REPO}...`);
|
|
74180
|
+
const result = await installSkill();
|
|
74181
|
+
if (result.success) {
|
|
74182
|
+
console.log(import_picocolors24.default.green(`
|
|
74183
|
+
✓ Skills installed successfully.`));
|
|
74184
|
+
} else {
|
|
74185
|
+
console.error(import_picocolors24.default.red(`
|
|
74186
|
+
Failed to install skill. Make sure npx is available.`));
|
|
74187
|
+
process.exit(1);
|
|
74188
|
+
}
|
|
74189
|
+
});
|
|
74190
|
+
cmd.command("remove").description("Remove the Dosu skills").action(() => {
|
|
74191
|
+
const installed = installedSkillNames();
|
|
74192
|
+
if (installed?.length === 0) {
|
|
74193
|
+
console.log(`No skills from ${SKILL_REPO} are installed.`);
|
|
74194
|
+
return;
|
|
73945
74195
|
}
|
|
73946
|
-
|
|
73947
|
-
|
|
73948
|
-
}
|
|
73949
|
-
return new Promise((resolve7, reject) => {
|
|
73950
|
-
let request;
|
|
73951
|
-
let response;
|
|
73952
|
-
let settled = false;
|
|
73953
|
-
const signal = init.signal;
|
|
73954
|
-
const cleanup = () => signal?.removeEventListener("abort", onAbort);
|
|
73955
|
-
const fail = (error61) => {
|
|
73956
|
-
if (settled)
|
|
73957
|
-
return;
|
|
73958
|
-
settled = true;
|
|
73959
|
-
cleanup();
|
|
73960
|
-
response?.destroy();
|
|
73961
|
-
request?.socket?.destroy();
|
|
73962
|
-
request?.destroy();
|
|
73963
|
-
reject(error61);
|
|
73964
|
-
};
|
|
73965
|
-
const succeed = (value) => {
|
|
73966
|
-
if (settled)
|
|
73967
|
-
return;
|
|
73968
|
-
settled = true;
|
|
73969
|
-
cleanup();
|
|
73970
|
-
response?.destroy();
|
|
73971
|
-
request?.socket?.destroy();
|
|
73972
|
-
request?.destroy();
|
|
73973
|
-
resolve7(value);
|
|
73974
|
-
};
|
|
73975
|
-
const onAbort = () => {
|
|
73976
|
-
const error61 = new Error("telemetry request aborted");
|
|
73977
|
-
error61.name = "AbortError";
|
|
73978
|
-
fail(error61);
|
|
73979
|
-
};
|
|
74196
|
+
const targets = installed ?? [SKILL_NAME];
|
|
74197
|
+
console.log(`Removing skills from ${SKILL_REPO}...`);
|
|
73980
74198
|
try {
|
|
73981
|
-
|
|
73982
|
-
|
|
73983
|
-
return;
|
|
73984
|
-
}
|
|
73985
|
-
const body = init.body;
|
|
73986
|
-
if (body !== undefined && body !== null && typeof body !== "string") {
|
|
73987
|
-
fail(new Error("unsupported telemetry request body"));
|
|
73988
|
-
return;
|
|
73989
|
-
}
|
|
73990
|
-
const headers = Object.fromEntries(new Headers(init.headers).entries());
|
|
73991
|
-
const transport = parsedURL.protocol === "https:" ? httpsRequest : httpRequest2;
|
|
73992
|
-
request = transport(parsedURL, {
|
|
73993
|
-
method: init.method ?? "GET",
|
|
73994
|
-
headers,
|
|
73995
|
-
agent: false
|
|
73996
|
-
}, (incoming) => {
|
|
73997
|
-
response = incoming;
|
|
73998
|
-
const chunks = [];
|
|
73999
|
-
let totalBytes = 0;
|
|
74000
|
-
incoming.on("data", (chunk) => {
|
|
74001
|
-
const bytes = Buffer.isBuffer(chunk) ? chunk : Buffer.from(chunk);
|
|
74002
|
-
totalBytes += bytes.byteLength;
|
|
74003
|
-
if (totalBytes > MAX_RESPONSE_BYTES) {
|
|
74004
|
-
fail(new Error("telemetry response too large"));
|
|
74005
|
-
return;
|
|
74006
|
-
}
|
|
74007
|
-
chunks.push(bytes);
|
|
74008
|
-
});
|
|
74009
|
-
incoming.once("error", fail);
|
|
74010
|
-
incoming.once("end", () => {
|
|
74011
|
-
try {
|
|
74012
|
-
const responseHeaders = new Headers;
|
|
74013
|
-
for (let index = 0;index < incoming.rawHeaders.length; index += 2) {
|
|
74014
|
-
const name = incoming.rawHeaders[index];
|
|
74015
|
-
const value = incoming.rawHeaders[index + 1];
|
|
74016
|
-
if (name && value)
|
|
74017
|
-
responseHeaders.append(name, value);
|
|
74018
|
-
}
|
|
74019
|
-
const status = incoming.statusCode;
|
|
74020
|
-
if (!status)
|
|
74021
|
-
throw new Error("telemetry response missing status");
|
|
74022
|
-
succeed(new Response(chunks.length > 0 ? Buffer.concat(chunks) : null, {
|
|
74023
|
-
status,
|
|
74024
|
-
statusText: incoming.statusMessage,
|
|
74025
|
-
headers: responseHeaders
|
|
74026
|
-
}));
|
|
74027
|
-
} catch (error61) {
|
|
74028
|
-
fail(error61);
|
|
74029
|
-
}
|
|
74030
|
-
});
|
|
74199
|
+
execSync2(`npx skills remove -g ${targets.join(" ")} -y`, {
|
|
74200
|
+
stdio: "inherit"
|
|
74031
74201
|
});
|
|
74032
|
-
|
|
74033
|
-
|
|
74034
|
-
|
|
74035
|
-
} catch
|
|
74036
|
-
|
|
74202
|
+
clearInstalledSha();
|
|
74203
|
+
console.log(import_picocolors24.default.green(`
|
|
74204
|
+
✓ Skills removed.`));
|
|
74205
|
+
} catch {
|
|
74206
|
+
console.error(import_picocolors24.default.red(`
|
|
74207
|
+
Failed to remove skills.`));
|
|
74208
|
+
process.exit(1);
|
|
74209
|
+
}
|
|
74210
|
+
});
|
|
74211
|
+
cmd.command("update").description("Update the Dosu skill to the latest version").action(async () => {
|
|
74212
|
+
console.log(`Updating skills from ${SKILL_REPO}...`);
|
|
74213
|
+
const result = await installSkill();
|
|
74214
|
+
if (!result.success) {
|
|
74215
|
+
console.error(import_picocolors24.default.red(`
|
|
74216
|
+
Failed to update skill.`));
|
|
74217
|
+
process.exit(1);
|
|
74037
74218
|
}
|
|
74219
|
+
console.log(import_picocolors24.default.green(`
|
|
74220
|
+
✓ Skills updated.`));
|
|
74038
74221
|
});
|
|
74222
|
+
return cmd;
|
|
74039
74223
|
}
|
|
74040
|
-
|
|
74041
|
-
|
|
74042
|
-
|
|
74043
|
-
|
|
74044
|
-
|
|
74224
|
+
var import_picocolors24, SKILL_REPO = "dosu-ai/dosu-skill", SKILL_NAME = "dosu", SAFE_SKILL_NAME, SUPPORTED_SKILL_AGENTS, SKILL_AGENT_BY_PROVIDER;
|
|
74225
|
+
var init_skill = __esm(() => {
|
|
74226
|
+
init_commander();
|
|
74227
|
+
init_logger();
|
|
74228
|
+
init_skill_update_check();
|
|
74229
|
+
import_picocolors24 = __toESM(require_picocolors(), 1);
|
|
74230
|
+
SAFE_SKILL_NAME = /^[a-zA-Z0-9][a-zA-Z0-9._-]*$/;
|
|
74231
|
+
SUPPORTED_SKILL_AGENTS = [
|
|
74232
|
+
"claude-code",
|
|
74233
|
+
"cursor",
|
|
74234
|
+
"gemini-cli",
|
|
74235
|
+
"codex",
|
|
74236
|
+
"windsurf",
|
|
74237
|
+
"zed",
|
|
74238
|
+
"cline",
|
|
74239
|
+
"github-copilot",
|
|
74240
|
+
"opencode",
|
|
74241
|
+
"antigravity"
|
|
74242
|
+
];
|
|
74243
|
+
SKILL_AGENT_BY_PROVIDER = {
|
|
74244
|
+
claude: "claude-code",
|
|
74245
|
+
cursor: "cursor",
|
|
74246
|
+
vscode: "github-copilot",
|
|
74247
|
+
gemini: "gemini-cli",
|
|
74248
|
+
codex: "codex",
|
|
74249
|
+
windsurf: "windsurf",
|
|
74250
|
+
zed: "zed",
|
|
74251
|
+
cline: "cline",
|
|
74252
|
+
"cline-cli": "cline",
|
|
74253
|
+
copilot: "github-copilot",
|
|
74254
|
+
opencode: "opencode",
|
|
74255
|
+
antigravity: "antigravity"
|
|
74256
|
+
};
|
|
74257
|
+
});
|
|
74258
|
+
|
|
74259
|
+
// src/telemetry/settings.ts
|
|
74260
|
+
import { randomUUID as randomUUID4 } from "node:crypto";
|
|
74261
|
+
import {
|
|
74262
|
+
existsSync as existsSync19,
|
|
74263
|
+
lstatSync as lstatSync2,
|
|
74264
|
+
mkdirSync as mkdirSync11,
|
|
74265
|
+
readFileSync as readFileSync19,
|
|
74266
|
+
renameSync as renameSync5,
|
|
74267
|
+
unlinkSync as unlinkSync3,
|
|
74268
|
+
writeFileSync as writeFileSync12
|
|
74269
|
+
} from "node:fs";
|
|
74270
|
+
import { join as join22 } from "node:path";
|
|
74271
|
+
function getTelemetrySettingsPath() {
|
|
74272
|
+
return join22(getConfigDir(), TELEMETRY_SETTINGS_FILENAME);
|
|
74273
|
+
}
|
|
74274
|
+
function loadTelemetrySettings() {
|
|
74275
|
+
return readTelemetrySettings().settings;
|
|
74276
|
+
}
|
|
74277
|
+
function setTelemetryEnabled(enabled) {
|
|
74278
|
+
if (typeof enabled !== "boolean")
|
|
74045
74279
|
return false;
|
|
74046
|
-
|
|
74047
|
-
|
|
74048
|
-
let timer;
|
|
74049
|
-
const timeout = new Promise((_resolve, reject) => {
|
|
74050
|
-
timer = setTimeout(() => {
|
|
74051
|
-
controller.abort();
|
|
74052
|
-
reject(new Error("telemetry request timed out"));
|
|
74053
|
-
}, REQUEST_TIMEOUT_MS);
|
|
74054
|
-
});
|
|
74055
|
-
try {
|
|
74056
|
-
const request = fetcher(url2, { ...init, redirect: "error", signal: controller.signal });
|
|
74057
|
-
const result = await Promise.race([request, timeout]);
|
|
74058
|
-
return result.ok;
|
|
74059
|
-
} catch {
|
|
74280
|
+
const current = readTelemetrySettings();
|
|
74281
|
+
if (current.status === "unsupported" || current.status === "error")
|
|
74060
74282
|
return false;
|
|
74061
|
-
|
|
74062
|
-
|
|
74063
|
-
|
|
74064
|
-
|
|
74283
|
+
const settings2 = current.status === "current" ? current.settings : emptyTelemetrySettings();
|
|
74284
|
+
if (enabled)
|
|
74285
|
+
delete settings2.disabled;
|
|
74286
|
+
else
|
|
74287
|
+
settings2.disabled = true;
|
|
74288
|
+
return writeTelemetrySettings(settings2);
|
|
74289
|
+
}
|
|
74290
|
+
function telemetryDisabledByEnvironment() {
|
|
74291
|
+
if (isEnabledEnvironmentFlag(process.env.DO_NOT_TRACK))
|
|
74292
|
+
return "DO_NOT_TRACK";
|
|
74293
|
+
if (isEnabledEnvironmentFlag(process.env.DOSU_TELEMETRY_DISABLED)) {
|
|
74294
|
+
return "DOSU_TELEMETRY_DISABLED";
|
|
74065
74295
|
}
|
|
74296
|
+
return;
|
|
74066
74297
|
}
|
|
74067
|
-
function
|
|
74068
|
-
if (
|
|
74298
|
+
function isTelemetryEnabled(settings2 = loadTelemetrySettings()) {
|
|
74299
|
+
if (telemetryDisabledByEnvironment() !== undefined)
|
|
74069
74300
|
return false;
|
|
74070
|
-
|
|
74071
|
-
return normalized !== "" && !["0", "false", "no", "off"].includes(normalized);
|
|
74072
|
-
}
|
|
74073
|
-
function runtimeNameAndMajor() {
|
|
74074
|
-
const versions2 = process.versions;
|
|
74075
|
-
const runtime = versions2.bun ? "bun" : "node";
|
|
74076
|
-
const version2 = versions2.bun ?? versions2.node;
|
|
74077
|
-
return { runtime, runtimeMajor: Number.parseInt(version2.split(".")[0] ?? "0", 10) || 0 };
|
|
74301
|
+
return settings2.disabled !== true;
|
|
74078
74302
|
}
|
|
74079
|
-
function
|
|
74080
|
-
const
|
|
74081
|
-
|
|
74082
|
-
|
|
74083
|
-
|
|
74084
|
-
|
|
74085
|
-
|
|
74086
|
-
|
|
74087
|
-
|
|
74088
|
-
|
|
74089
|
-
|
|
74090
|
-
});
|
|
74303
|
+
function getOrCreateInstallID() {
|
|
74304
|
+
const current = readTelemetrySettings();
|
|
74305
|
+
if (current.status === "current" && current.settings.install_id) {
|
|
74306
|
+
return current.settings.install_id;
|
|
74307
|
+
}
|
|
74308
|
+
if (current.status !== "current" && current.status !== "missing")
|
|
74309
|
+
return;
|
|
74310
|
+
const installID = randomUUID4();
|
|
74311
|
+
const settings2 = current.status === "current" ? current.settings : emptyTelemetrySettings();
|
|
74312
|
+
settings2.install_id = installID;
|
|
74313
|
+
return writeTelemetrySettings(settings2) ? installID : undefined;
|
|
74091
74314
|
}
|
|
74092
|
-
function
|
|
74093
|
-
|
|
74094
|
-
|
|
74095
|
-
|
|
74096
|
-
|
|
74097
|
-
|
|
74098
|
-
|
|
74099
|
-
return
|
|
74315
|
+
function resetInstallID() {
|
|
74316
|
+
const current = readTelemetrySettings();
|
|
74317
|
+
if (current.status !== "current" && current.status !== "missing")
|
|
74318
|
+
return;
|
|
74319
|
+
const settings2 = current.status === "current" ? current.settings : emptyTelemetrySettings();
|
|
74320
|
+
const installID = randomUUID4();
|
|
74321
|
+
settings2.install_id = installID;
|
|
74322
|
+
return writeTelemetrySettings(settings2) ? installID : undefined;
|
|
74100
74323
|
}
|
|
74101
|
-
function
|
|
74324
|
+
function readTelemetrySettings() {
|
|
74325
|
+
const path3 = getTelemetrySettingsPath();
|
|
74326
|
+
let file2;
|
|
74102
74327
|
try {
|
|
74103
|
-
|
|
74104
|
-
|
|
74328
|
+
file2 = lstatSync2(path3);
|
|
74329
|
+
} catch (error61) {
|
|
74330
|
+
if (isErrorCode(error61, "ENOENT")) {
|
|
74331
|
+
return { status: "missing", settings: emptyTelemetrySettings() };
|
|
74332
|
+
}
|
|
74333
|
+
return { status: "error", settings: disabledTelemetrySettings() };
|
|
74334
|
+
}
|
|
74335
|
+
if (!file2.isFile() || file2.size > MAX_TELEMETRY_SETTINGS_BYTES) {
|
|
74336
|
+
return { status: "invalid", settings: disabledTelemetrySettings() };
|
|
74337
|
+
}
|
|
74338
|
+
let content;
|
|
74339
|
+
try {
|
|
74340
|
+
content = readFileSync19(path3, "utf-8");
|
|
74105
74341
|
} catch {
|
|
74106
|
-
return
|
|
74342
|
+
return { status: "error", settings: disabledTelemetrySettings() };
|
|
74107
74343
|
}
|
|
74108
|
-
|
|
74109
|
-
function safeUuid(generate) {
|
|
74344
|
+
let raw;
|
|
74110
74345
|
try {
|
|
74111
|
-
|
|
74112
|
-
return isUUID2(value) ? value : undefined;
|
|
74346
|
+
raw = JSON.parse(content);
|
|
74113
74347
|
} catch {
|
|
74114
|
-
return;
|
|
74348
|
+
return { status: "invalid", settings: disabledTelemetrySettings() };
|
|
74115
74349
|
}
|
|
74116
|
-
|
|
74117
|
-
|
|
74118
|
-
|
|
74119
|
-
|
|
74120
|
-
}
|
|
74121
|
-
function createCommandTelemetry(settings2, dependencies = {}) {
|
|
74122
|
-
const env = dependencies.env ?? process.env;
|
|
74123
|
-
const fetcher = dependencies.fetch;
|
|
74124
|
-
const now = dependencies.now ?? Date.now;
|
|
74125
|
-
const generateUuid = dependencies.randomUUID ?? randomUUID4;
|
|
74126
|
-
const writeStderr = dependencies.stderr ?? ((payload) => process.stderr.write(`${payload}
|
|
74127
|
-
`));
|
|
74128
|
-
const webAppURL = dependencies.webAppURL ?? getWebAppURL;
|
|
74129
|
-
const runtime = resolveRuntime(dependencies, env);
|
|
74130
|
-
const debug = env.DOSU_TELEMETRY_DEBUG === "1";
|
|
74131
|
-
const disabled = settings2.disabled === true || environmentFlag(env.DO_NOT_TRACK) || environmentFlag(env.DOSU_TELEMETRY_DISABLED);
|
|
74132
|
-
const rawAnalyticsToken = dependencies.env ? env.DOSU_POSTHOG_PROJECT_TOKEN_OVERRIDE ?? env.DOSU_POSTHOG_PROJECT_TOKEN : process.env.DOSU_POSTHOG_PROJECT_TOKEN_OVERRIDE ?? "phc_Uo9RxhN0HRk8gZkPlT18BCr7eqsM0MPWVW88HZB95V5";
|
|
74133
|
-
const rawSentryDsn = dependencies.env ? env.DOSU_CLI_SENTRY_DSN_OVERRIDE ?? env.DOSU_CLI_SENTRY_DSN : process.env.DOSU_CLI_SENTRY_DSN_OVERRIDE ?? "https://f6b3e51dea6f50746316aa1896df76e0@o4505364738670592.ingest.us.sentry.io/4511883653283840";
|
|
74134
|
-
const analyticsToken = parsePostHogProjectToken(rawAnalyticsToken) ?? undefined;
|
|
74135
|
-
const sentryDsn = parseSentryDsn(rawSentryDsn)?.dsn;
|
|
74136
|
-
let started = false;
|
|
74137
|
-
let terminal = false;
|
|
74138
|
-
let startedAt = 0;
|
|
74139
|
-
let command = UNKNOWN_COMMAND;
|
|
74140
|
-
let context = normalizeContext(undefined);
|
|
74141
|
-
let installIdResolved = false;
|
|
74142
|
-
let resolvedInstallId = settings2.install_id;
|
|
74143
|
-
function installId() {
|
|
74144
|
-
if (isUUID2(resolvedInstallId))
|
|
74145
|
-
return resolvedInstallId;
|
|
74146
|
-
if (installIdResolved)
|
|
74147
|
-
return;
|
|
74148
|
-
installIdResolved = true;
|
|
74149
|
-
try {
|
|
74150
|
-
resolvedInstallId = dependencies.resolveInstallId?.();
|
|
74151
|
-
} catch {
|
|
74152
|
-
resolvedInstallId = undefined;
|
|
74153
|
-
}
|
|
74154
|
-
return isUUID2(resolvedInstallId) ? resolvedInstallId : undefined;
|
|
74350
|
+
if (!isRecord5(raw))
|
|
74351
|
+
return { status: "invalid", settings: disabledTelemetrySettings() };
|
|
74352
|
+
if (raw.schema_version !== TELEMETRY_SCHEMA_VERSION) {
|
|
74353
|
+
const status = "schema_version" in raw ? "unsupported" : "invalid";
|
|
74354
|
+
return { status, settings: disabledTelemetrySettings() };
|
|
74155
74355
|
}
|
|
74156
|
-
|
|
74157
|
-
|
|
74158
|
-
|
|
74159
|
-
|
|
74356
|
+
if ("disabled" in raw && typeof raw.disabled !== "boolean") {
|
|
74357
|
+
return { status: "invalid", settings: disabledTelemetrySettings() };
|
|
74358
|
+
}
|
|
74359
|
+
const settings2 = emptyTelemetrySettings();
|
|
74360
|
+
if (raw.disabled === true)
|
|
74361
|
+
settings2.disabled = true;
|
|
74362
|
+
if (typeof raw.install_id === "string" && isUUID2(raw.install_id)) {
|
|
74363
|
+
settings2.install_id = raw.install_id;
|
|
74160
74364
|
}
|
|
74161
|
-
|
|
74365
|
+
return { status: "current", settings: settings2 };
|
|
74366
|
+
}
|
|
74367
|
+
function writeTelemetrySettings(settings2) {
|
|
74368
|
+
const path3 = getTelemetrySettingsPath();
|
|
74369
|
+
const dir = getConfigDir();
|
|
74370
|
+
const temporaryPath = `${path3}.${process.pid}.${randomUUID4()}.tmp`;
|
|
74371
|
+
try {
|
|
74372
|
+
if (!existsSync19(dir))
|
|
74373
|
+
mkdirSync11(dir, { recursive: true, mode: 448 });
|
|
74374
|
+
writeFileSync12(temporaryPath, `${JSON.stringify(settings2, null, 2)}
|
|
74375
|
+
`, {
|
|
74376
|
+
flag: "wx",
|
|
74377
|
+
mode: 384
|
|
74378
|
+
});
|
|
74379
|
+
renameSync5(temporaryPath, path3);
|
|
74380
|
+
return true;
|
|
74381
|
+
} catch {
|
|
74162
74382
|
try {
|
|
74163
|
-
|
|
74164
|
-
if (!disabled && analyticsToken) {
|
|
74165
|
-
const id2 = context.user?.id ?? installId();
|
|
74166
|
-
if (id2) {
|
|
74167
|
-
const payload = buildPostHogPayload({
|
|
74168
|
-
apiKey: analyticsToken,
|
|
74169
|
-
...context.user ? {} : { installId: id2 },
|
|
74170
|
-
command,
|
|
74171
|
-
result,
|
|
74172
|
-
durationMs: safeNow(now) - startedAt,
|
|
74173
|
-
exitCode,
|
|
74174
|
-
errorCode: error61?.code,
|
|
74175
|
-
context,
|
|
74176
|
-
runtime
|
|
74177
|
-
});
|
|
74178
|
-
const body = JSON.stringify(payload);
|
|
74179
|
-
if (debug) {
|
|
74180
|
-
debugPayload(body);
|
|
74181
|
-
} else {
|
|
74182
|
-
const base = parseTelemetryWebAppURL(webAppURL());
|
|
74183
|
-
if (base) {
|
|
74184
|
-
tasks.push(sendHttpsRequest(`${base}/ph-api/i/v0/e/`, {
|
|
74185
|
-
method: "POST",
|
|
74186
|
-
headers: { "content-type": "application/json" },
|
|
74187
|
-
body
|
|
74188
|
-
}, fetcher));
|
|
74189
|
-
}
|
|
74190
|
-
}
|
|
74191
|
-
}
|
|
74192
|
-
}
|
|
74193
|
-
if (!disabled && result === "failure" && error61 && shouldSendToSentry(error61) && sentryDsn) {
|
|
74194
|
-
const id2 = eventId(generateUuid);
|
|
74195
|
-
const envelope = id2 ? buildSentryEnvelope({
|
|
74196
|
-
dsn: sentryDsn,
|
|
74197
|
-
command,
|
|
74198
|
-
context,
|
|
74199
|
-
runtime,
|
|
74200
|
-
error: error61,
|
|
74201
|
-
eventId: id2,
|
|
74202
|
-
timestampMs: safeNow(now),
|
|
74203
|
-
debugId: BUNDLE_DEBUG_ID_PLACEHOLDER
|
|
74204
|
-
}) : null;
|
|
74205
|
-
if (envelope) {
|
|
74206
|
-
if (debug) {
|
|
74207
|
-
debugPayload(envelope.body);
|
|
74208
|
-
} else {
|
|
74209
|
-
tasks.push(sendHttpsRequest(envelope.endpoint, {
|
|
74210
|
-
method: "POST",
|
|
74211
|
-
headers: { "content-type": "application/x-sentry-envelope" },
|
|
74212
|
-
body: envelope.body
|
|
74213
|
-
}, fetcher));
|
|
74214
|
-
}
|
|
74215
|
-
}
|
|
74216
|
-
}
|
|
74217
|
-
await Promise.all(tasks);
|
|
74383
|
+
unlinkSync3(temporaryPath);
|
|
74218
74384
|
} catch {}
|
|
74385
|
+
return false;
|
|
74219
74386
|
}
|
|
74220
|
-
return {
|
|
74221
|
-
start(commandName, commandContext) {
|
|
74222
|
-
if (started || terminal)
|
|
74223
|
-
return;
|
|
74224
|
-
started = true;
|
|
74225
|
-
command = canonicalCommand(commandName);
|
|
74226
|
-
context = normalizeContext(commandContext);
|
|
74227
|
-
startedAt = safeNow(now);
|
|
74228
|
-
},
|
|
74229
|
-
async complete(exitCode = 0) {
|
|
74230
|
-
if (!started || terminal)
|
|
74231
|
-
return;
|
|
74232
|
-
terminal = true;
|
|
74233
|
-
const normalizedExitCode = normalizeExitCode(exitCode, 0);
|
|
74234
|
-
const result = normalizedExitCode === 0 ? "success" : validationFailure(normalizedExitCode, undefined) ? "validation_error" : "failure";
|
|
74235
|
-
const error61 = result === "failure" ? { type: "CommandExitError", frames: [], exitCode: normalizedExitCode } : undefined;
|
|
74236
|
-
await dispatch(result, normalizedExitCode, error61);
|
|
74237
|
-
},
|
|
74238
|
-
async fail(rawError) {
|
|
74239
|
-
if (!started || terminal)
|
|
74240
|
-
return;
|
|
74241
|
-
terminal = true;
|
|
74242
|
-
const error61 = sanitizeError(rawError);
|
|
74243
|
-
const exitCode = error61.exitCode ?? 1;
|
|
74244
|
-
const result = validationFailure(exitCode, error61) ? "validation_error" : "failure";
|
|
74245
|
-
await dispatch(result, exitCode, error61);
|
|
74246
|
-
}
|
|
74247
|
-
};
|
|
74248
74387
|
}
|
|
74249
|
-
function
|
|
74250
|
-
return
|
|
74388
|
+
function emptyTelemetrySettings() {
|
|
74389
|
+
return { schema_version: TELEMETRY_SCHEMA_VERSION };
|
|
74251
74390
|
}
|
|
74252
|
-
|
|
74253
|
-
|
|
74254
|
-
|
|
74255
|
-
|
|
74256
|
-
|
|
74257
|
-
|
|
74258
|
-
|
|
74259
|
-
|
|
74260
|
-
|
|
74261
|
-
|
|
74262
|
-
|
|
74263
|
-
|
|
74264
|
-
|
|
74265
|
-
|
|
74266
|
-
|
|
74267
|
-
|
|
74268
|
-
|
|
74269
|
-
|
|
74270
|
-
|
|
74271
|
-
|
|
74272
|
-
|
|
74273
|
-
|
|
74274
|
-
|
|
74275
|
-
|
|
74276
|
-
"BAD_REQUEST",
|
|
74277
|
-
"CLIENT_CLOSED_REQUEST",
|
|
74278
|
-
"CONFLICT",
|
|
74279
|
-
"EACCES",
|
|
74280
|
-
"EAI_AGAIN",
|
|
74281
|
-
"EBUSY",
|
|
74282
|
-
"ECONNREFUSED",
|
|
74283
|
-
"ECONNRESET",
|
|
74284
|
-
"EEXIST",
|
|
74285
|
-
"EHOSTUNREACH",
|
|
74286
|
-
"EINVAL",
|
|
74287
|
-
"EISDIR",
|
|
74288
|
-
"ELOOP",
|
|
74289
|
-
"EMFILE",
|
|
74290
|
-
"ENAMETOOLONG",
|
|
74291
|
-
"ENETUNREACH",
|
|
74292
|
-
"ENFILE",
|
|
74293
|
-
"ENOENT",
|
|
74294
|
-
"ENOMEM",
|
|
74295
|
-
"ENOSPC",
|
|
74296
|
-
"ENOTFOUND",
|
|
74297
|
-
"ENOTDIR",
|
|
74298
|
-
"EPIPE",
|
|
74299
|
-
"EPERM",
|
|
74300
|
-
"EROFS",
|
|
74301
|
-
"ETIMEDOUT",
|
|
74302
|
-
"FORBIDDEN",
|
|
74303
|
-
"GATEWAY_TIMEOUT",
|
|
74304
|
-
"IMPORT_ALREADY_IN_PROGRESS",
|
|
74305
|
-
"INTERNAL_SERVER_ERROR",
|
|
74306
|
-
"INVALID_ARGUMENT",
|
|
74307
|
-
"METHOD_NOT_SUPPORTED",
|
|
74308
|
-
"NOT_FOUND",
|
|
74309
|
-
"NOT_IMPLEMENTED",
|
|
74310
|
-
"PARSE_ERROR",
|
|
74311
|
-
"PAYLOAD_TOO_LARGE",
|
|
74312
|
-
"PAYMENT_REQUIRED",
|
|
74313
|
-
"PRECONDITION_FAILED",
|
|
74314
|
-
"SERVICE_UNAVAILABLE",
|
|
74315
|
-
"SESSION_EXPIRED",
|
|
74316
|
-
"SESSION_PERSISTENCE_ERROR",
|
|
74317
|
-
"SESSION_REFRESH_ERROR",
|
|
74318
|
-
"TIMEOUT",
|
|
74319
|
-
"TOO_MANY_REQUESTS",
|
|
74320
|
-
"UNAUTHORIZED",
|
|
74321
|
-
"UNPROCESSABLE_CONTENT",
|
|
74322
|
-
"UNSUPPORTED_MEDIA_TYPE",
|
|
74323
|
-
"VALIDATION_ERROR",
|
|
74324
|
-
"commander.conflictingOption",
|
|
74325
|
-
"commander.error",
|
|
74326
|
-
"commander.excessArguments",
|
|
74327
|
-
"commander.invalidArgument",
|
|
74328
|
-
"commander.missingArgument",
|
|
74329
|
-
"commander.missingMandatoryOptionValue",
|
|
74330
|
-
"commander.optionMissingArgument",
|
|
74331
|
-
"commander.unknownCommand",
|
|
74332
|
-
"commander.unknownOption"
|
|
74333
|
-
]);
|
|
74391
|
+
function disabledTelemetrySettings() {
|
|
74392
|
+
return { schema_version: TELEMETRY_SCHEMA_VERSION, disabled: true };
|
|
74393
|
+
}
|
|
74394
|
+
function isEnabledEnvironmentFlag(value) {
|
|
74395
|
+
if (value === undefined)
|
|
74396
|
+
return false;
|
|
74397
|
+
const normalized = value.trim().toLowerCase();
|
|
74398
|
+
if (normalized === "")
|
|
74399
|
+
return false;
|
|
74400
|
+
return !["0", "false", "no", "off"].includes(normalized);
|
|
74401
|
+
}
|
|
74402
|
+
function isUUID2(value) {
|
|
74403
|
+
return /^[0-9a-f]{8}-[0-9a-f]{4}-[1-8][0-9a-f]{3}-[89ab][0-9a-f]{3}-[0-9a-f]{12}$/i.test(value);
|
|
74404
|
+
}
|
|
74405
|
+
function isRecord5(value) {
|
|
74406
|
+
return typeof value === "object" && value !== null && !Array.isArray(value);
|
|
74407
|
+
}
|
|
74408
|
+
function isErrorCode(error61, code) {
|
|
74409
|
+
return typeof error61 === "object" && error61 !== null && "code" in error61 && error61.code === code;
|
|
74410
|
+
}
|
|
74411
|
+
var TELEMETRY_SCHEMA_VERSION = 1, TELEMETRY_SETTINGS_FILENAME = "telemetry.json", MAX_TELEMETRY_SETTINGS_BYTES;
|
|
74412
|
+
var init_settings = __esm(() => {
|
|
74413
|
+
init_config();
|
|
74414
|
+
MAX_TELEMETRY_SETTINGS_BYTES = 16 * 1024;
|
|
74334
74415
|
});
|
|
74335
74416
|
|
|
74336
74417
|
// src/debug/follow.ts
|
|
@@ -77048,6 +77129,7 @@ async function stepOfferInitialSync(cfg) {
|
|
|
77048
77129
|
const outcome = await runKnowledgeSync({ bootstrap: true });
|
|
77049
77130
|
if (outcome.status !== "backlog" || outcome.readySessions === 0) {
|
|
77050
77131
|
s.stop("No past agent sessions to study. Dosu will learn from new ones as you work.");
|
|
77132
|
+
recordCommandFacets({ backfill_offer: "not-offered" });
|
|
77051
77133
|
return;
|
|
77052
77134
|
}
|
|
77053
77135
|
const n5 = outcome.readySessions;
|
|
@@ -77067,10 +77149,12 @@ async function stepOfferInitialSync(cfg) {
|
|
|
77067
77149
|
initialValue: true
|
|
77068
77150
|
});
|
|
77069
77151
|
if (isCancel2(mineNow) || !mineNow) {
|
|
77152
|
+
recordCommandFacets({ backfill_offer: isCancel2(mineNow) ? "cancelled" : "declined" });
|
|
77070
77153
|
log2.info(`Skipped. Dosu studies new sessions in the background as you work; run ${info("dosu knowledge sync")} anytime to study these too.`);
|
|
77071
77154
|
return;
|
|
77072
77155
|
}
|
|
77073
77156
|
if (spawnDetachedSelf(["knowledge", "sync", "--quiet", "--bootstrap"])) {
|
|
77157
|
+
recordCommandFacets({ backfill_offer: "accepted" });
|
|
77074
77158
|
log2.success(`\uD83D\uDCDA Studying ${n5} session${n5 === 1 ? "" : "s"} in the background. Watch progress on the Activity screen; when it finishes, the new notes are in Dosu and agents connected to this MCP can use them.`);
|
|
77075
77159
|
const watch = await confirm2({
|
|
77076
77160
|
message: "What next?",
|
|
@@ -77081,6 +77165,7 @@ async function stepOfferInitialSync(cfg) {
|
|
|
77081
77165
|
if (!isCancel2(watch) && watch)
|
|
77082
77166
|
await runActivityView();
|
|
77083
77167
|
} else {
|
|
77168
|
+
recordCommandFacets({ backfill_offer: "spawn-failed" });
|
|
77084
77169
|
log2.warn(`Could not start the background sync. Run ${info("dosu knowledge sync")} manually.`);
|
|
77085
77170
|
}
|
|
77086
77171
|
}
|
|
@@ -77818,6 +77903,7 @@ var init_flow2 = __esm(() => {
|
|
|
77818
77903
|
init_providers();
|
|
77819
77904
|
init_detach();
|
|
77820
77905
|
init_sync();
|
|
77906
|
+
init_telemetry();
|
|
77821
77907
|
init_activity_view();
|
|
77822
77908
|
init_layout();
|
|
77823
77909
|
init_prompts();
|
|
@@ -82665,8 +82751,9 @@ init_detach();
|
|
|
82665
82751
|
init_status();
|
|
82666
82752
|
init_sync();
|
|
82667
82753
|
init_watermark();
|
|
82754
|
+
init_telemetry();
|
|
82668
82755
|
var import_picocolors18 = __toESM(require_picocolors(), 1);
|
|
82669
|
-
import { existsSync as
|
|
82756
|
+
import { existsSync as existsSync17 } from "node:fs";
|
|
82670
82757
|
import { homedir as homedir5 } from "node:os";
|
|
82671
82758
|
import { delimiter as delimiter4, join as join19 } from "node:path";
|
|
82672
82759
|
function requireConfig7() {
|
|
@@ -82776,7 +82863,9 @@ ${results.length - opts.limit} more results not shown.`));
|
|
|
82776
82863
|
}
|
|
82777
82864
|
});
|
|
82778
82865
|
cmd.command("sync").description("Scan local agent session history and report the study backlog").option("--quiet", "Background mode for hooks: honor backoff, exit 0, print nothing").option("--detach", "Re-spawn detached and return immediately (used by agent hooks)").option("--bootstrap", "Backfill mode: study the full local session history regardless of age and drain the backlog (used by setup)").option("--status", "Show whether a sync is running now, plus watermark and recent activity").option("--report", "Write the same HTML harvest report as the log-to-dosu-knowledge skill and open it").option("--out <path>", "HTML report path (default: tmp/dosu-knowledge-report.html)").option("--json", "Output as JSON").action(async (opts) => {
|
|
82866
|
+
const trigger = opts.bootstrap ? "bootstrap" : opts.quiet ? "hook" : "manual";
|
|
82779
82867
|
if (opts.status) {
|
|
82868
|
+
recordCommandFacets({ sync_trigger: trigger, sync_status: "status-only" });
|
|
82780
82869
|
const status = getSyncStatus();
|
|
82781
82870
|
if (opts.json) {
|
|
82782
82871
|
printResult(status, opts);
|
|
@@ -82786,7 +82875,7 @@ ${results.length - opts.limit} more results not shown.`));
|
|
|
82786
82875
|
return;
|
|
82787
82876
|
}
|
|
82788
82877
|
if (opts.detach) {
|
|
82789
|
-
spawnDetachedSelf([
|
|
82878
|
+
const spawned = spawnDetachedSelf([
|
|
82790
82879
|
"knowledge",
|
|
82791
82880
|
"sync",
|
|
82792
82881
|
...opts.quiet ? ["--quiet"] : [],
|
|
@@ -82794,6 +82883,10 @@ ${results.length - opts.limit} more results not shown.`));
|
|
|
82794
82883
|
...opts.report ? ["--report"] : [],
|
|
82795
82884
|
...opts.out ? ["--out", opts.out] : []
|
|
82796
82885
|
]);
|
|
82886
|
+
recordCommandFacets({
|
|
82887
|
+
sync_trigger: trigger,
|
|
82888
|
+
sync_status: spawned ? "detached" : "detach-failed"
|
|
82889
|
+
});
|
|
82797
82890
|
return;
|
|
82798
82891
|
}
|
|
82799
82892
|
const deps = { mine: buildLearner(opts.quiet ? "hook" : "manual") };
|
|
@@ -82802,14 +82895,25 @@ ${results.length - opts.limit} more results not shown.`));
|
|
|
82802
82895
|
bootstrap: opts.bootstrap,
|
|
82803
82896
|
deps
|
|
82804
82897
|
});
|
|
82898
|
+
let sessionsStudied = outcome.studiedSessions ?? 0;
|
|
82899
|
+
let notesWritten = outcome.learner?.notesWritten ?? 0;
|
|
82805
82900
|
if (opts.bootstrap && deps.mine) {
|
|
82806
82901
|
const maxRounds = Math.ceil(outcome.readySessions / MINE_BATCH_LIMIT) + 2;
|
|
82807
82902
|
for (let round = 1;outcome.status === "studied" && round < maxRounds; round++) {
|
|
82808
82903
|
if (!opts.quiet && !opts.json)
|
|
82809
82904
|
printSyncOutcome(outcome);
|
|
82810
82905
|
outcome = await runKnowledgeSync({ quiet: opts.quiet, bootstrap: true, deps });
|
|
82906
|
+
sessionsStudied += outcome.studiedSessions ?? 0;
|
|
82907
|
+
notesWritten += outcome.learner?.notesWritten ?? 0;
|
|
82811
82908
|
}
|
|
82812
82909
|
}
|
|
82910
|
+
recordCommandFacets({
|
|
82911
|
+
sync_trigger: trigger,
|
|
82912
|
+
sync_status: outcome.status,
|
|
82913
|
+
sessions_studied: sessionsStudied,
|
|
82914
|
+
notes_written: notesWritten,
|
|
82915
|
+
...outcome.learner ? { learner_outcome: outcome.learner.outcome } : {}
|
|
82916
|
+
});
|
|
82813
82917
|
if (opts.quiet)
|
|
82814
82918
|
return;
|
|
82815
82919
|
if (opts.json) {
|
|
@@ -83079,7 +83183,7 @@ function reportHookFailure(agent, err) {
|
|
|
83079
83183
|
}
|
|
83080
83184
|
function dosuOnPath() {
|
|
83081
83185
|
const bin = process.platform === "win32" ? "dosu.cmd" : "dosu";
|
|
83082
|
-
return (process.env.PATH ?? "").split(delimiter4).some((dir) => dir !== "" &&
|
|
83186
|
+
return (process.env.PATH ?? "").split(delimiter4).some((dir) => dir !== "" && existsSync17(join19(dir, bin)));
|
|
83083
83187
|
}
|
|
83084
83188
|
|
|
83085
83189
|
// src/commands/libraries.ts
|
|
@@ -83871,8 +83975,8 @@ function sourcesCommand() {
|
|
|
83871
83975
|
}
|
|
83872
83976
|
console.log(import_picocolors25.default.dim("Waiting for the install to complete..."));
|
|
83873
83977
|
}
|
|
83874
|
-
const timedOut = new Promise((
|
|
83875
|
-
timeoutId = setTimeout(() =>
|
|
83978
|
+
const timedOut = new Promise((resolve7) => {
|
|
83979
|
+
timeoutId = setTimeout(() => resolve7(null), timeoutSeconds * 1000);
|
|
83876
83980
|
});
|
|
83877
83981
|
const result = await Promise.race([installationPromise, timedOut]);
|
|
83878
83982
|
if (result === null) {
|
|
@@ -84988,4 +85092,4 @@ execute().catch((err) => {
|
|
|
84988
85092
|
process.exit(1);
|
|
84989
85093
|
});
|
|
84990
85094
|
|
|
84991
|
-
//# debugId=
|
|
85095
|
+
//# debugId=d4304c43-c40e-f448-6475-6e2164756e21
|