@promptscript/cli 1.16.0 → 1.17.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/bin/prs.js +1 -1
- package/index.js +1382 -115
- package/package.json +3 -3
- package/src/cli.d.ts +1 -1
- package/src/cli.d.ts.map +1 -1
- package/src/commands/telemetry.d.ts +3 -0
- package/src/commands/telemetry.d.ts.map +1 -0
- package/src/config/env-config.d.ts +1 -0
- package/src/config/env-config.d.ts.map +1 -1
- package/src/config/merge-config.d.ts.map +1 -1
- package/src/telemetry/config.d.ts +10 -0
- package/src/telemetry/config.d.ts.map +1 -0
- package/src/telemetry/session.d.ts +14 -0
- package/src/telemetry/session.d.ts.map +1 -0
package/index.js
CHANGED
|
@@ -4437,8 +4437,8 @@ var init_services = __esm({
|
|
|
4437
4437
|
existsSync,
|
|
4438
4438
|
readFileSync: readFileSync2,
|
|
4439
4439
|
rename: async (...args) => {
|
|
4440
|
-
const { rename:
|
|
4441
|
-
return
|
|
4440
|
+
const { rename: rename3 } = await import("node:fs/promises");
|
|
4441
|
+
return rename3(...args);
|
|
4442
4442
|
},
|
|
4443
4443
|
rm: async (...args) => {
|
|
4444
4444
|
const { rm: rm3 } = await import("node:fs/promises");
|
|
@@ -18437,8 +18437,8 @@ ${f.content}`
|
|
|
18437
18437
|
if (project) sections.push(project);
|
|
18438
18438
|
const techStack = this.techStack(ast, renderer);
|
|
18439
18439
|
if (techStack) sections.push(techStack);
|
|
18440
|
-
const
|
|
18441
|
-
if (
|
|
18440
|
+
const architecture2 = this.architecture(ast, renderer);
|
|
18441
|
+
if (architecture2) sections.push(architecture2);
|
|
18442
18442
|
const contextSection = this.contextSection(ast, renderer);
|
|
18443
18443
|
if (contextSection) sections.push(contextSection);
|
|
18444
18444
|
const codeStandards = this.codeStandards(ast, renderer);
|
|
@@ -20437,8 +20437,8 @@ ${content}`);
|
|
|
20437
20437
|
if (intro) sections.push(intro);
|
|
20438
20438
|
const techStack = this.techStack(ast);
|
|
20439
20439
|
if (techStack) sections.push(techStack);
|
|
20440
|
-
const
|
|
20441
|
-
if (
|
|
20440
|
+
const architecture2 = this.architecture(ast);
|
|
20441
|
+
if (architecture2) sections.push(architecture2);
|
|
20442
20442
|
const context = this.contextSection(ast);
|
|
20443
20443
|
if (context) sections.push(context);
|
|
20444
20444
|
const codeStyle = this.codeStyle(ast);
|
|
@@ -21021,8 +21021,8 @@ ${content}`);
|
|
|
21021
21021
|
if (identity) sections.push(identity);
|
|
21022
21022
|
const techStack = this.techStack(ast, renderer);
|
|
21023
21023
|
if (techStack) sections.push(techStack);
|
|
21024
|
-
const
|
|
21025
|
-
if (
|
|
21024
|
+
const architecture2 = this.architecture(ast, renderer);
|
|
21025
|
+
if (architecture2) sections.push(architecture2);
|
|
21026
21026
|
const contextSection = this.contextSection(ast, renderer);
|
|
21027
21027
|
if (contextSection) sections.push(contextSection);
|
|
21028
21028
|
const codeStandards = this.codeStandards(ast, renderer);
|
|
@@ -26597,8 +26597,20 @@ function loadEnvOverrides() {
|
|
|
26597
26597
|
const registryUrl = process.env["PROMPTSCRIPT_REGISTRY_URL"];
|
|
26598
26598
|
const cacheTtl = process.env["PROMPTSCRIPT_CACHE_TTL"];
|
|
26599
26599
|
const cacheEnabled = process.env["PROMPTSCRIPT_CACHE_ENABLED"];
|
|
26600
|
+
const telemetry = process.env["PROMPTSCRIPT_TELEMETRY"];
|
|
26600
26601
|
const hasCache = cacheTtl || cacheEnabled;
|
|
26601
26602
|
const hasRegistry = gitUrl || gitRef || registryUrl || hasCache;
|
|
26603
|
+
if (!hasRegistry && telemetry === void 0) {
|
|
26604
|
+
return overrides;
|
|
26605
|
+
}
|
|
26606
|
+
if (telemetry !== void 0) {
|
|
26607
|
+
const normalized = telemetry.trim().toLowerCase();
|
|
26608
|
+
if (["1", "true", "yes", "on"].includes(normalized)) {
|
|
26609
|
+
overrides.telemetry = true;
|
|
26610
|
+
} else if (["0", "false", "no", "off"].includes(normalized)) {
|
|
26611
|
+
overrides.telemetry = false;
|
|
26612
|
+
}
|
|
26613
|
+
}
|
|
26602
26614
|
if (!hasRegistry) {
|
|
26603
26615
|
return overrides;
|
|
26604
26616
|
}
|
|
@@ -26677,6 +26689,22 @@ function userConfigToProjectRegistry(userRegistry) {
|
|
|
26677
26689
|
}
|
|
26678
26690
|
function mergeConfigs(userConfig, projectConfig, envOverrides, cliFlags) {
|
|
26679
26691
|
const merged = { ...projectConfig };
|
|
26692
|
+
const telemetryValues = [
|
|
26693
|
+
userConfig.telemetry,
|
|
26694
|
+
projectConfig.telemetry,
|
|
26695
|
+
envOverrides.telemetry,
|
|
26696
|
+
cliFlags?.telemetry
|
|
26697
|
+
];
|
|
26698
|
+
if (telemetryValues.includes(false)) {
|
|
26699
|
+
merged.telemetry = false;
|
|
26700
|
+
} else {
|
|
26701
|
+
const explicitTelemetry = telemetryValues.find(
|
|
26702
|
+
(value) => value !== void 0
|
|
26703
|
+
);
|
|
26704
|
+
if (explicitTelemetry !== void 0) {
|
|
26705
|
+
merged.telemetry = explicitTelemetry;
|
|
26706
|
+
}
|
|
26707
|
+
}
|
|
26680
26708
|
if (userConfig.registry) {
|
|
26681
26709
|
const userRegistry = userConfigToProjectRegistry(userConfig.registry);
|
|
26682
26710
|
merged.registry = deepMergeRegistry(userRegistry, merged.registry);
|
|
@@ -31082,9 +31110,9 @@ var require_rate_limit = __commonJS({
|
|
|
31082
31110
|
throw new Error("The configured rate-limit store does not implement a `read` method, which is required to use `{ increment: false }`");
|
|
31083
31111
|
}
|
|
31084
31112
|
try {
|
|
31085
|
-
const res = await new Promise((
|
|
31113
|
+
const res = await new Promise((resolve38, reject) => {
|
|
31086
31114
|
store[storeMethod](key, (err, res2) => {
|
|
31087
|
-
err ? reject(err) :
|
|
31115
|
+
err ? reject(err) : resolve38(res2);
|
|
31088
31116
|
}, timeWindow, max);
|
|
31089
31117
|
});
|
|
31090
31118
|
current = res.current;
|
|
@@ -31191,13 +31219,13 @@ var init_health = __esm({
|
|
|
31191
31219
|
});
|
|
31192
31220
|
|
|
31193
31221
|
// packages/server/src/source-dirs.ts
|
|
31194
|
-
import { readFile as
|
|
31195
|
-
import { join as
|
|
31222
|
+
import { readFile as readFile29 } from "fs/promises";
|
|
31223
|
+
import { join as join31, dirname as dirname23 } from "path";
|
|
31196
31224
|
import { parse as parseYaml16 } from "yaml";
|
|
31197
31225
|
async function readProjectConfig(workspace) {
|
|
31198
31226
|
for (const filename of CONFIG_FILENAMES) {
|
|
31199
31227
|
try {
|
|
31200
|
-
const raw = await
|
|
31228
|
+
const raw = await readFile29(join31(workspace, filename), "utf-8");
|
|
31201
31229
|
const config = parseYaml16(raw);
|
|
31202
31230
|
if (config) return config;
|
|
31203
31231
|
} catch {
|
|
@@ -31208,10 +31236,10 @@ async function readProjectConfig(workspace) {
|
|
|
31208
31236
|
async function resolveSourceDir(workspace) {
|
|
31209
31237
|
for (const filename of CONFIG_FILENAMES) {
|
|
31210
31238
|
try {
|
|
31211
|
-
const raw = await
|
|
31239
|
+
const raw = await readFile29(join31(workspace, filename), "utf-8");
|
|
31212
31240
|
const config = parseYaml16(raw);
|
|
31213
31241
|
if (config?.input?.entry) {
|
|
31214
|
-
return
|
|
31242
|
+
return dirname23(config.input.entry);
|
|
31215
31243
|
}
|
|
31216
31244
|
} catch {
|
|
31217
31245
|
}
|
|
@@ -31225,9 +31253,9 @@ async function resolveFileGlobs(workspace) {
|
|
|
31225
31253
|
async function resolveWatchPaths(workspace) {
|
|
31226
31254
|
const sourceDir = await resolveSourceDir(workspace);
|
|
31227
31255
|
return [
|
|
31228
|
-
|
|
31229
|
-
|
|
31230
|
-
|
|
31256
|
+
join31(workspace, sourceDir),
|
|
31257
|
+
join31(workspace, "promptscript.yaml"),
|
|
31258
|
+
join31(workspace, "promptscript.yml")
|
|
31231
31259
|
];
|
|
31232
31260
|
}
|
|
31233
31261
|
var DEFAULT_SOURCE_DIR, CONFIG_FILENAMES;
|
|
@@ -31254,16 +31282,16 @@ var init_config2 = __esm({
|
|
|
31254
31282
|
});
|
|
31255
31283
|
|
|
31256
31284
|
// packages/server/src/path-guard.ts
|
|
31257
|
-
import { resolve as
|
|
31285
|
+
import { resolve as resolve36, relative as relative16, isAbsolute as isAbsolute16, dirname as dirname24, basename as basename18 } from "path";
|
|
31258
31286
|
import { realpath as realpath5 } from "fs/promises";
|
|
31259
31287
|
async function resolveSafePath(workspace, requestedPath) {
|
|
31260
31288
|
const decoded = decodeURIComponent(requestedPath);
|
|
31261
|
-
if (
|
|
31289
|
+
if (isAbsolute16(decoded)) {
|
|
31262
31290
|
throw new PathTraversalError(requestedPath);
|
|
31263
31291
|
}
|
|
31264
|
-
const resolved =
|
|
31292
|
+
const resolved = resolve36(workspace, decoded);
|
|
31265
31293
|
const rel = relative16(workspace, resolved);
|
|
31266
|
-
if (rel.startsWith("..") ||
|
|
31294
|
+
if (rel.startsWith("..") || isAbsolute16(rel)) {
|
|
31267
31295
|
throw new PathTraversalError(requestedPath);
|
|
31268
31296
|
}
|
|
31269
31297
|
let realWorkspace;
|
|
@@ -31278,8 +31306,8 @@ async function resolveSafePath(workspace, requestedPath) {
|
|
|
31278
31306
|
} catch (err) {
|
|
31279
31307
|
if (err instanceof Error && "code" in err && err.code === "ENOENT") {
|
|
31280
31308
|
try {
|
|
31281
|
-
const realParent = await realpath5(
|
|
31282
|
-
real =
|
|
31309
|
+
const realParent = await realpath5(dirname24(resolved));
|
|
31310
|
+
real = resolve36(realParent, basename18(resolved));
|
|
31283
31311
|
} catch {
|
|
31284
31312
|
return resolved;
|
|
31285
31313
|
}
|
|
@@ -31288,7 +31316,7 @@ async function resolveSafePath(workspace, requestedPath) {
|
|
|
31288
31316
|
}
|
|
31289
31317
|
}
|
|
31290
31318
|
const realRel = relative16(realWorkspace, real);
|
|
31291
|
-
if (realRel.startsWith("..") ||
|
|
31319
|
+
if (realRel.startsWith("..") || isAbsolute16(realRel)) {
|
|
31292
31320
|
throw new PathTraversalError(requestedPath);
|
|
31293
31321
|
}
|
|
31294
31322
|
return resolved;
|
|
@@ -31307,8 +31335,8 @@ var init_path_guard = __esm({
|
|
|
31307
31335
|
});
|
|
31308
31336
|
|
|
31309
31337
|
// packages/server/src/routes/files.ts
|
|
31310
|
-
import { readFile as
|
|
31311
|
-
import { dirname as
|
|
31338
|
+
import { readFile as readFile30, writeFile as writeFile13, unlink as unlink3, stat as stat3, mkdir as mkdir12 } from "fs/promises";
|
|
31339
|
+
import { dirname as dirname25 } from "path";
|
|
31312
31340
|
import fg from "fast-glob";
|
|
31313
31341
|
function registerRoutes(app, workspace, readOnly) {
|
|
31314
31342
|
app.get("/api/files", async () => {
|
|
@@ -31328,7 +31356,7 @@ function registerRoutes(app, workspace, readOnly) {
|
|
|
31328
31356
|
const filePath = request.params["*"];
|
|
31329
31357
|
try {
|
|
31330
31358
|
const resolved = await resolveSafePath(workspace, filePath);
|
|
31331
|
-
const content = await
|
|
31359
|
+
const content = await readFile30(resolved, "utf-8");
|
|
31332
31360
|
return { path: filePath, content };
|
|
31333
31361
|
} catch (err) {
|
|
31334
31362
|
if (err instanceof PathTraversalError) {
|
|
@@ -31345,7 +31373,7 @@ function registerRoutes(app, workspace, readOnly) {
|
|
|
31345
31373
|
const filePath = request.params["*"];
|
|
31346
31374
|
try {
|
|
31347
31375
|
const resolved = await resolveSafePath(workspace, filePath);
|
|
31348
|
-
await
|
|
31376
|
+
await writeFile13(resolved, request.body.content, "utf-8");
|
|
31349
31377
|
return { path: filePath, status: "updated" };
|
|
31350
31378
|
} catch (err) {
|
|
31351
31379
|
if (err instanceof PathTraversalError) {
|
|
@@ -31368,8 +31396,8 @@ function registerRoutes(app, workspace, readOnly) {
|
|
|
31368
31396
|
return reply.status(409).send({ error: "File already exists" });
|
|
31369
31397
|
} catch {
|
|
31370
31398
|
}
|
|
31371
|
-
await
|
|
31372
|
-
await
|
|
31399
|
+
await mkdir12(dirname25(resolved), { recursive: true });
|
|
31400
|
+
await writeFile13(resolved, request.body.content, "utf-8");
|
|
31373
31401
|
return reply.status(201).send({ path: filePath, status: "created" });
|
|
31374
31402
|
} catch (err) {
|
|
31375
31403
|
if (err instanceof PathTraversalError) {
|
|
@@ -31597,7 +31625,7 @@ var migrate_exports = {};
|
|
|
31597
31625
|
__export(migrate_exports, {
|
|
31598
31626
|
migrateCommand: () => migrateCommand
|
|
31599
31627
|
});
|
|
31600
|
-
import { dirname as
|
|
31628
|
+
import { dirname as dirname26, relative as relative18, resolve as resolve37 } from "path";
|
|
31601
31629
|
import { parse as parseYaml17 } from "yaml";
|
|
31602
31630
|
async function migrateCommand(options, services = createDefaultServices()) {
|
|
31603
31631
|
const previousOutputStream = getContext().outputStream;
|
|
@@ -31617,7 +31645,7 @@ async function runMigrateCommand(options, services) {
|
|
|
31617
31645
|
ConsoleOutput.info("No migration candidates found. No files changed.");
|
|
31618
31646
|
return;
|
|
31619
31647
|
}
|
|
31620
|
-
const configPath =
|
|
31648
|
+
const configPath = findProjectConfig2(services);
|
|
31621
31649
|
if (!configPath) {
|
|
31622
31650
|
await initCommand(
|
|
31623
31651
|
{
|
|
@@ -31730,7 +31758,7 @@ function validateOptions(options) {
|
|
|
31730
31758
|
throw new Error("--static and --llm cannot be used together");
|
|
31731
31759
|
}
|
|
31732
31760
|
}
|
|
31733
|
-
function
|
|
31761
|
+
function findProjectConfig2(services) {
|
|
31734
31762
|
const envConfig = process.env["PROMPTSCRIPT_CONFIG"];
|
|
31735
31763
|
if (envConfig && services.fs.existsSync(envConfig)) {
|
|
31736
31764
|
return envConfig;
|
|
@@ -31781,7 +31809,7 @@ function extractTargetNames(targets) {
|
|
|
31781
31809
|
}
|
|
31782
31810
|
async function createStaticMigrationWrites(candidates, config, services) {
|
|
31783
31811
|
const result = await importMultipleFiles(
|
|
31784
|
-
candidates.map((candidate) =>
|
|
31812
|
+
candidates.map((candidate) => resolve37(services.cwd, candidate.path)),
|
|
31785
31813
|
{
|
|
31786
31814
|
projectName: config.id,
|
|
31787
31815
|
syntaxVersion: config.syntax || getLatestSyntaxVersion()
|
|
@@ -31873,7 +31901,7 @@ ${content}`;
|
|
|
31873
31901
|
}
|
|
31874
31902
|
function toUsePath(entryPath, targetPath) {
|
|
31875
31903
|
const withoutExtension = targetPath.replace(/\.prs$/, "");
|
|
31876
|
-
const path2 = relative18(
|
|
31904
|
+
const path2 = relative18(dirname26(entryPath), withoutExtension).replace(/\\/g, "/");
|
|
31877
31905
|
return path2.startsWith(".") ? path2 : `./${path2}`;
|
|
31878
31906
|
}
|
|
31879
31907
|
function validateProjectPath(path2) {
|
|
@@ -31933,7 +31961,7 @@ init_src();
|
|
|
31933
31961
|
init_init();
|
|
31934
31962
|
import { Command } from "commander";
|
|
31935
31963
|
import { fileURLToPath as fileURLToPath4, pathToFileURL } from "url";
|
|
31936
|
-
import { dirname as
|
|
31964
|
+
import { dirname as dirname27 } from "path";
|
|
31937
31965
|
|
|
31938
31966
|
// packages/cli/src/commands/compile.ts
|
|
31939
31967
|
import { resolve as resolve19, dirname as dirname15, isAbsolute as isAbsolute13, relative as relative13 } from "path";
|
|
@@ -31942,7 +31970,7 @@ import { chmod, writeFile as writeFile4, mkdir as mkdir5, readFile as readFile15
|
|
|
31942
31970
|
import { existsSync as existsSync13 } from "fs";
|
|
31943
31971
|
import chokidar from "chokidar";
|
|
31944
31972
|
|
|
31945
|
-
// node_modules/.pnpm/balanced-match@4.0.
|
|
31973
|
+
// node_modules/.pnpm/balanced-match@4.0.3/node_modules/balanced-match/dist/esm/index.js
|
|
31946
31974
|
var balanced = (a, b, str) => {
|
|
31947
31975
|
const ma = a instanceof RegExp ? maybeMatch(a, str) : a;
|
|
31948
31976
|
const mb = b instanceof RegExp ? maybeMatch(b, str) : b;
|
|
@@ -38017,7 +38045,7 @@ async function discoverNativeContent(dir) {
|
|
|
38017
38045
|
|
|
38018
38046
|
// packages/resolver/src/vendor-manifest.ts
|
|
38019
38047
|
import { createHash as createHash3 } from "crypto";
|
|
38020
|
-
import { execFile } from "child_process";
|
|
38048
|
+
import { execFile, spawn } from "child_process";
|
|
38021
38049
|
import { lstat as lstat5, readFile as readFile12, readdir as readdir4, realpath as realpath3 } from "fs/promises";
|
|
38022
38050
|
import { basename as basename10, dirname as dirname9, isAbsolute as isAbsolute8, join as join9, posix as posix2, relative as relative7, sep as sep5 } from "path";
|
|
38023
38051
|
import { promisify } from "util";
|
|
@@ -38025,6 +38053,8 @@ var VENDOR_MANIFEST_FILE = ".vendor-manifest.json";
|
|
|
38025
38053
|
var VENDOR_GIT_DIR = ".promptscript-git";
|
|
38026
38054
|
var execFileAsync = promisify(execFile);
|
|
38027
38055
|
var NULL_DEVICE = process.platform === "win32" ? "NUL" : "/dev/null";
|
|
38056
|
+
var MAX_GIT_STDERR_BYTES = 64 * 1024;
|
|
38057
|
+
var MAX_GIT_TREE_RECORD_BYTES = 1024 * 1024;
|
|
38028
38058
|
function getVendorRepositoryRelativePath(repoUrl) {
|
|
38029
38059
|
const sshMatch = /^git@([^:]+):(.+)$/.exec(repoUrl);
|
|
38030
38060
|
const candidate = sshMatch ? `https://${sshMatch[1]}/${sshMatch[2]}` : /^[a-z][a-z\d+.-]*:\/\//i.test(repoUrl) ? repoUrl : `https://${repoUrl}`;
|
|
@@ -38141,25 +38171,91 @@ async function verifyGitRepositoryCheckout(directory, gitDirectoryName, expected
|
|
|
38141
38171
|
if (head.stdout.trim().toLowerCase() !== expectedCommit.toLowerCase()) {
|
|
38142
38172
|
throw new Error(`Vendored repository commit does not match the lockfile: ${directory}`);
|
|
38143
38173
|
}
|
|
38144
|
-
const tree = await execFileAsync("git", [...commonArgs, "ls-tree", "-r", "-z", expectedCommit], {
|
|
38145
|
-
env: environment
|
|
38146
|
-
});
|
|
38147
38174
|
const trackedFiles = /* @__PURE__ */ new Map();
|
|
38148
|
-
|
|
38149
|
-
|
|
38150
|
-
|
|
38151
|
-
|
|
38152
|
-
|
|
38153
|
-
|
|
38154
|
-
|
|
38155
|
-
|
|
38156
|
-
|
|
38157
|
-
throw new Error(`Unsupported Git tree mode in vendored repository: ${path2}`);
|
|
38158
|
-
}
|
|
38159
|
-
trackedFiles.set(path2, {
|
|
38160
|
-
objectId: metadata[2],
|
|
38161
|
-
executable: mode === "100755"
|
|
38175
|
+
const treeProcess = spawn("git", [...commonArgs, "ls-tree", "-r", "-z", expectedCommit], {
|
|
38176
|
+
env: environment,
|
|
38177
|
+
stdio: ["ignore", "pipe", "pipe"]
|
|
38178
|
+
});
|
|
38179
|
+
let processError = null;
|
|
38180
|
+
const processCompletion = new Promise((resolveProcess) => {
|
|
38181
|
+
treeProcess.once("error", (error) => {
|
|
38182
|
+
processError = new Error(`Failed to run git ls-tree: ${error.message}`, { cause: error });
|
|
38183
|
+
resolveProcess({ code: null, signal: null });
|
|
38162
38184
|
});
|
|
38185
|
+
treeProcess.once("close", (code2, signal2) => {
|
|
38186
|
+
resolveProcess({ code: code2, signal: signal2 });
|
|
38187
|
+
});
|
|
38188
|
+
});
|
|
38189
|
+
const stderrChunks = [];
|
|
38190
|
+
let stderrBytes = 0;
|
|
38191
|
+
let stderrTruncated = false;
|
|
38192
|
+
const stderrCompletion = (async () => {
|
|
38193
|
+
for await (const chunk of treeProcess.stderr) {
|
|
38194
|
+
const remaining = MAX_GIT_STDERR_BYTES - stderrBytes;
|
|
38195
|
+
if (remaining > 0) {
|
|
38196
|
+
const captured = chunk.subarray(0, remaining);
|
|
38197
|
+
stderrChunks.push(Buffer.from(captured));
|
|
38198
|
+
stderrBytes += captured.length;
|
|
38199
|
+
}
|
|
38200
|
+
if (chunk.length > remaining) {
|
|
38201
|
+
stderrTruncated = true;
|
|
38202
|
+
}
|
|
38203
|
+
}
|
|
38204
|
+
})();
|
|
38205
|
+
let pendingRecord = Buffer.alloc(0);
|
|
38206
|
+
let stdoutError = null;
|
|
38207
|
+
try {
|
|
38208
|
+
for await (const chunk of treeProcess.stdout) {
|
|
38209
|
+
const output = pendingRecord.length > 0 ? Buffer.concat([pendingRecord, chunk]) : chunk;
|
|
38210
|
+
let recordStart = 0;
|
|
38211
|
+
let separator = output.indexOf(0, recordStart);
|
|
38212
|
+
while (separator >= 0) {
|
|
38213
|
+
if (separator > recordStart) {
|
|
38214
|
+
const row = output.toString("utf8", recordStart, separator);
|
|
38215
|
+
const metadataSeparator = row.indexOf(" ");
|
|
38216
|
+
const metadata = row.slice(0, metadataSeparator).split(" ");
|
|
38217
|
+
const path2 = row.slice(metadataSeparator + 1);
|
|
38218
|
+
const [mode, entryType, objectId, ...extraFields] = metadata;
|
|
38219
|
+
if (metadataSeparator < 0 || entryType !== "blob" || extraFields.length > 0 || mode === void 0 || objectId === void 0) {
|
|
38220
|
+
throw new Error(`Unsupported Git tree entry in vendored repository: ${path2}`);
|
|
38221
|
+
}
|
|
38222
|
+
if (mode !== "100644" && mode !== "100755") {
|
|
38223
|
+
throw new Error(`Unsupported Git tree mode in vendored repository: ${path2}`);
|
|
38224
|
+
}
|
|
38225
|
+
trackedFiles.set(path2, {
|
|
38226
|
+
objectId,
|
|
38227
|
+
executable: mode === "100755"
|
|
38228
|
+
});
|
|
38229
|
+
}
|
|
38230
|
+
recordStart = separator + 1;
|
|
38231
|
+
separator = output.indexOf(0, recordStart);
|
|
38232
|
+
}
|
|
38233
|
+
pendingRecord = recordStart < output.length ? Buffer.from(output.subarray(recordStart)) : Buffer.alloc(0);
|
|
38234
|
+
if (pendingRecord.length > MAX_GIT_TREE_RECORD_BYTES) {
|
|
38235
|
+
throw new Error(`Git ls-tree record exceeds ${MAX_GIT_TREE_RECORD_BYTES} bytes`);
|
|
38236
|
+
}
|
|
38237
|
+
}
|
|
38238
|
+
} catch (error) {
|
|
38239
|
+
stdoutError = error instanceof Error ? error : new Error(String(error), { cause: error });
|
|
38240
|
+
treeProcess.kill();
|
|
38241
|
+
}
|
|
38242
|
+
const [{ code, signal }] = await Promise.all([processCompletion, stderrCompletion]);
|
|
38243
|
+
if (processError) {
|
|
38244
|
+
throw processError;
|
|
38245
|
+
}
|
|
38246
|
+
if (stdoutError) {
|
|
38247
|
+
throw stdoutError;
|
|
38248
|
+
}
|
|
38249
|
+
if (code !== 0) {
|
|
38250
|
+
const stderrOutput = Buffer.concat(stderrChunks, stderrBytes).toString("utf8");
|
|
38251
|
+
const stderrDetails = stderrTruncated ? `${stderrOutput}
|
|
38252
|
+
[stderr truncated]` : stderrOutput;
|
|
38253
|
+
const details = stderrDetails.trim();
|
|
38254
|
+
const status = signal ? `signal ${signal}` : `exit code ${code ?? "unknown"}`;
|
|
38255
|
+
throw new Error(`Git ls-tree failed with ${status}${details ? `: ${details}` : ""}`);
|
|
38256
|
+
}
|
|
38257
|
+
if (pendingRecord.length > 0) {
|
|
38258
|
+
throw new Error("Git ls-tree output ended with an unterminated record");
|
|
38163
38259
|
}
|
|
38164
38260
|
const worktreeFiles = [];
|
|
38165
38261
|
async function collectFiles(currentDirectory, prefix) {
|
|
@@ -39380,7 +39476,7 @@ var HttpRegistry = class {
|
|
|
39380
39476
|
* Sleep for specified milliseconds.
|
|
39381
39477
|
*/
|
|
39382
39478
|
sleep(ms) {
|
|
39383
|
-
return new Promise((
|
|
39479
|
+
return new Promise((resolve38) => setTimeout(resolve38, ms));
|
|
39384
39480
|
}
|
|
39385
39481
|
/**
|
|
39386
39482
|
* Fetch with timeout.
|
|
@@ -39422,7 +39518,7 @@ var HttpRegistry = class {
|
|
|
39422
39518
|
*/
|
|
39423
39519
|
async fetchWithRetry(url) {
|
|
39424
39520
|
let lastError;
|
|
39425
|
-
let
|
|
39521
|
+
let delay2 = this.initialDelay;
|
|
39426
39522
|
for (let attempt = 0; attempt <= this.maxRetries; attempt++) {
|
|
39427
39523
|
try {
|
|
39428
39524
|
const response = await this.fetchWithTimeout(url, {
|
|
@@ -39441,8 +39537,8 @@ var HttpRegistry = class {
|
|
|
39441
39537
|
lastError = error;
|
|
39442
39538
|
}
|
|
39443
39539
|
if (attempt < this.maxRetries) {
|
|
39444
|
-
await this.sleep(
|
|
39445
|
-
|
|
39540
|
+
await this.sleep(delay2);
|
|
39541
|
+
delay2 *= 2;
|
|
39446
39542
|
}
|
|
39447
39543
|
}
|
|
39448
39544
|
throw lastError ?? new Error("Unknown fetch error");
|
|
@@ -44700,12 +44796,12 @@ var Compiler = class _Compiler {
|
|
|
44700
44796
|
*/
|
|
44701
44797
|
async watch(entryPath, options = {}) {
|
|
44702
44798
|
const { default: chokidar2 } = await import("chokidar");
|
|
44703
|
-
const { dirname:
|
|
44704
|
-
const baseDir =
|
|
44799
|
+
const { dirname: dirname28, resolve: resolve38 } = await import("path");
|
|
44800
|
+
const baseDir = dirname28(resolve38(entryPath));
|
|
44705
44801
|
const includePatterns = options.include ?? ["**/*.prs"];
|
|
44706
44802
|
const excludePatterns = options.exclude ?? ["**/node_modules/**"];
|
|
44707
44803
|
const debounceMs = options.debounce ?? 300;
|
|
44708
|
-
const watchPatterns = includePatterns.map((p) =>
|
|
44804
|
+
const watchPatterns = includePatterns.map((p) => resolve38(baseDir, p));
|
|
44709
44805
|
let debounceTimer = null;
|
|
44710
44806
|
let pendingChanges = [];
|
|
44711
44807
|
const handleChange = async (changedFiles) => {
|
|
@@ -44843,7 +44939,7 @@ var Compiler = class _Compiler {
|
|
|
44843
44939
|
};
|
|
44844
44940
|
|
|
44845
44941
|
// packages/cli/src/output/pager.ts
|
|
44846
|
-
import { spawn } from "child_process";
|
|
44942
|
+
import { spawn as spawn2 } from "child_process";
|
|
44847
44943
|
function isTTY() {
|
|
44848
44944
|
return process.stdout.isTTY === true;
|
|
44849
44945
|
}
|
|
@@ -44880,7 +44976,7 @@ var Pager = class {
|
|
|
44880
44976
|
}
|
|
44881
44977
|
return;
|
|
44882
44978
|
}
|
|
44883
|
-
return new Promise((
|
|
44979
|
+
return new Promise((resolve38) => {
|
|
44884
44980
|
const pagerCmd = getPagerCommand();
|
|
44885
44981
|
const [cmd, ...args] = pagerCmd.split(" ");
|
|
44886
44982
|
if (cmd === "less" && !args.includes("-R")) {
|
|
@@ -44888,10 +44984,10 @@ var Pager = class {
|
|
|
44888
44984
|
}
|
|
44889
44985
|
if (!cmd) {
|
|
44890
44986
|
console.log(content);
|
|
44891
|
-
|
|
44987
|
+
resolve38();
|
|
44892
44988
|
return;
|
|
44893
44989
|
}
|
|
44894
|
-
const pager =
|
|
44990
|
+
const pager = spawn2(cmd, args, {
|
|
44895
44991
|
stdio: ["pipe", "inherit", "inherit"],
|
|
44896
44992
|
env: {
|
|
44897
44993
|
...process.env,
|
|
@@ -44901,17 +44997,17 @@ var Pager = class {
|
|
|
44901
44997
|
});
|
|
44902
44998
|
pager.on("error", (_err) => {
|
|
44903
44999
|
console.log(content);
|
|
44904
|
-
|
|
45000
|
+
resolve38();
|
|
44905
45001
|
});
|
|
44906
45002
|
pager.on("close", () => {
|
|
44907
|
-
|
|
45003
|
+
resolve38();
|
|
44908
45004
|
});
|
|
44909
45005
|
if (pager.stdin) {
|
|
44910
45006
|
pager.stdin.write(content);
|
|
44911
45007
|
pager.stdin.end();
|
|
44912
45008
|
} else {
|
|
44913
45009
|
console.log(content);
|
|
44914
|
-
|
|
45010
|
+
resolve38();
|
|
44915
45011
|
}
|
|
44916
45012
|
});
|
|
44917
45013
|
}
|
|
@@ -45098,7 +45194,7 @@ function detectOutputConflicts(targets) {
|
|
|
45098
45194
|
|
|
45099
45195
|
// packages/cli/src/utils/managed-output-cleanup.ts
|
|
45100
45196
|
import { constants as constants2 } from "node:fs";
|
|
45101
|
-
import { execFile as execFile2, spawn as
|
|
45197
|
+
import { execFile as execFile2, spawn as spawn3 } from "node:child_process";
|
|
45102
45198
|
import { createHash as createHash4 } from "node:crypto";
|
|
45103
45199
|
import {
|
|
45104
45200
|
lstat as lstat8,
|
|
@@ -45565,7 +45661,7 @@ async function removeHookOutputIfUnchanged(file, outputRoot, expectedContent) {
|
|
|
45565
45661
|
async function guardedRewrite(directory, name, directoryStat, fileStat, expectedContent, content) {
|
|
45566
45662
|
const expectedHash = createHash4("sha256").update(expectedContent).digest("hex");
|
|
45567
45663
|
return new Promise((resolveResult) => {
|
|
45568
|
-
const child =
|
|
45664
|
+
const child = spawn3(
|
|
45569
45665
|
process.execPath,
|
|
45570
45666
|
[
|
|
45571
45667
|
"-e",
|
|
@@ -45603,7 +45699,7 @@ async function guardedRewrite(directory, name, directoryStat, fileStat, expected
|
|
|
45603
45699
|
}
|
|
45604
45700
|
async function guardedCreate(directory, name, directoryStat, content, mode) {
|
|
45605
45701
|
return new Promise((resolveResult) => {
|
|
45606
|
-
const child =
|
|
45702
|
+
const child = spawn3(
|
|
45607
45703
|
process.execPath,
|
|
45608
45704
|
[
|
|
45609
45705
|
"-e",
|
|
@@ -50741,14 +50837,1169 @@ Usage: echo '{"file_path":"..."}' | prs hook ${action}
|
|
|
50741
50837
|
// packages/cli/src/cli.ts
|
|
50742
50838
|
init_hooks();
|
|
50743
50839
|
|
|
50840
|
+
// packages/cli/src/commands/telemetry.ts
|
|
50841
|
+
init_console();
|
|
50842
|
+
|
|
50843
|
+
// packages/cli/src/telemetry/config.ts
|
|
50844
|
+
import { existsSync as existsSync27 } from "node:fs";
|
|
50845
|
+
import { mkdir as mkdir11, readFile as readFile27, rename as rename2, writeFile as writeFile11 } from "node:fs/promises";
|
|
50846
|
+
import { dirname as dirname21, isAbsolute as isAbsolute15, join as join29, resolve as resolve34 } from "node:path";
|
|
50847
|
+
import { parseDocument } from "yaml";
|
|
50848
|
+
|
|
50849
|
+
// packages/telemetry/src/config.ts
|
|
50850
|
+
import { homedir as homedir6 } from "node:os";
|
|
50851
|
+
import { join as join27 } from "node:path";
|
|
50852
|
+
var DEFAULT_TELEMETRY_ENDPOINT = "https://telemetry.guziak.net/v1/events";
|
|
50853
|
+
function isDisabled(value) {
|
|
50854
|
+
if (value === void 0) {
|
|
50855
|
+
return false;
|
|
50856
|
+
}
|
|
50857
|
+
return ["0", "false", "no", "off"].includes(value.trim().toLowerCase());
|
|
50858
|
+
}
|
|
50859
|
+
function isDoNotTrackEnabled(value) {
|
|
50860
|
+
if (value === void 0) {
|
|
50861
|
+
return false;
|
|
50862
|
+
}
|
|
50863
|
+
const normalized = value.trim();
|
|
50864
|
+
return normalized !== "" && normalized !== "0";
|
|
50865
|
+
}
|
|
50866
|
+
function isSecureEndpoint(value) {
|
|
50867
|
+
try {
|
|
50868
|
+
const url = new URL(value);
|
|
50869
|
+
return url.protocol === "https:" && url.username === "" && url.password === "";
|
|
50870
|
+
} catch {
|
|
50871
|
+
return false;
|
|
50872
|
+
}
|
|
50873
|
+
}
|
|
50874
|
+
function resolveTelemetryConfig(input2 = {}) {
|
|
50875
|
+
const environment = input2.environment ?? process.env;
|
|
50876
|
+
const endpoint = input2.endpoint ?? environment["PROMPTSCRIPT_TELEMETRY_ENDPOINT"] ?? DEFAULT_TELEMETRY_ENDPOINT;
|
|
50877
|
+
const vetoes = [];
|
|
50878
|
+
if (isDoNotTrackEnabled(environment["DO_NOT_TRACK"])) {
|
|
50879
|
+
vetoes.push("DO_NOT_TRACK");
|
|
50880
|
+
}
|
|
50881
|
+
if (isDisabled(environment["PROMPTSCRIPT_TELEMETRY"])) {
|
|
50882
|
+
vetoes.push("PROMPTSCRIPT_TELEMETRY");
|
|
50883
|
+
}
|
|
50884
|
+
if (input2.userEnabled === false) {
|
|
50885
|
+
vetoes.push("user config");
|
|
50886
|
+
}
|
|
50887
|
+
if (input2.projectEnabled === false) {
|
|
50888
|
+
vetoes.push("project config");
|
|
50889
|
+
}
|
|
50890
|
+
if (input2.configurationValid === false) {
|
|
50891
|
+
vetoes.push("configuration unavailable");
|
|
50892
|
+
}
|
|
50893
|
+
if (!isSecureEndpoint(endpoint)) {
|
|
50894
|
+
vetoes.push("insecure endpoint");
|
|
50895
|
+
}
|
|
50896
|
+
return {
|
|
50897
|
+
enabled: vetoes.length === 0,
|
|
50898
|
+
endpoint,
|
|
50899
|
+
cacheDirectory: input2.cacheDirectory ?? join27(homedir6(), ".promptscript", ".cache"),
|
|
50900
|
+
vetoes
|
|
50901
|
+
};
|
|
50902
|
+
}
|
|
50903
|
+
|
|
50904
|
+
// packages/telemetry/telemetry-schema/promptscript-v1.json
|
|
50905
|
+
var promptscript_v1_default = {
|
|
50906
|
+
eventSchema: 1,
|
|
50907
|
+
commands: [
|
|
50908
|
+
"init",
|
|
50909
|
+
"compile",
|
|
50910
|
+
"build",
|
|
50911
|
+
"validate",
|
|
50912
|
+
"inspect",
|
|
50913
|
+
"pull",
|
|
50914
|
+
"diff",
|
|
50915
|
+
"check",
|
|
50916
|
+
"update-check",
|
|
50917
|
+
"serve",
|
|
50918
|
+
"import",
|
|
50919
|
+
"migrate",
|
|
50920
|
+
"upgrade",
|
|
50921
|
+
"lock",
|
|
50922
|
+
"update",
|
|
50923
|
+
"vendor-sync",
|
|
50924
|
+
"vendor-check",
|
|
50925
|
+
"resolve",
|
|
50926
|
+
"hooks",
|
|
50927
|
+
"skills-add",
|
|
50928
|
+
"skills-remove",
|
|
50929
|
+
"skills-list",
|
|
50930
|
+
"skills-update",
|
|
50931
|
+
"registry",
|
|
50932
|
+
"other"
|
|
50933
|
+
],
|
|
50934
|
+
outcomes: ["success", "error", "cancelled"],
|
|
50935
|
+
features: ["dry_run", "watch", "strict", "ci", "build_profile"],
|
|
50936
|
+
targets: [
|
|
50937
|
+
"github",
|
|
50938
|
+
"claude",
|
|
50939
|
+
"cursor",
|
|
50940
|
+
"antigravity",
|
|
50941
|
+
"factory",
|
|
50942
|
+
"opencode",
|
|
50943
|
+
"gemini",
|
|
50944
|
+
"windsurf",
|
|
50945
|
+
"cline",
|
|
50946
|
+
"roo",
|
|
50947
|
+
"codex",
|
|
50948
|
+
"continue",
|
|
50949
|
+
"augment",
|
|
50950
|
+
"goose",
|
|
50951
|
+
"kilo",
|
|
50952
|
+
"amp",
|
|
50953
|
+
"trae",
|
|
50954
|
+
"junie",
|
|
50955
|
+
"kiro",
|
|
50956
|
+
"cortex",
|
|
50957
|
+
"crush",
|
|
50958
|
+
"command-code",
|
|
50959
|
+
"kode",
|
|
50960
|
+
"mcpjam",
|
|
50961
|
+
"mistral-vibe",
|
|
50962
|
+
"mux",
|
|
50963
|
+
"openhands",
|
|
50964
|
+
"pi",
|
|
50965
|
+
"qoder",
|
|
50966
|
+
"qwen-code",
|
|
50967
|
+
"zencoder",
|
|
50968
|
+
"neovate",
|
|
50969
|
+
"pochi",
|
|
50970
|
+
"adal",
|
|
50971
|
+
"iflow",
|
|
50972
|
+
"openclaw",
|
|
50973
|
+
"codebuddy",
|
|
50974
|
+
"aider",
|
|
50975
|
+
"amazon-q",
|
|
50976
|
+
"warp",
|
|
50977
|
+
"zed",
|
|
50978
|
+
"jules",
|
|
50979
|
+
"devin",
|
|
50980
|
+
"grok",
|
|
50981
|
+
"kimi",
|
|
50982
|
+
"mimo",
|
|
50983
|
+
"deep-agents",
|
|
50984
|
+
"forgecode"
|
|
50985
|
+
],
|
|
50986
|
+
limits: {
|
|
50987
|
+
maxEvents: 25,
|
|
50988
|
+
maxCount: 1e6,
|
|
50989
|
+
maxDurationSumMs: 1e12
|
|
50990
|
+
}
|
|
50991
|
+
};
|
|
50992
|
+
|
|
50993
|
+
// packages/telemetry/src/schema.ts
|
|
50994
|
+
var TELEMETRY_EVENT_SCHEMA = promptscript_v1_default.eventSchema;
|
|
50995
|
+
var MAX_EVENTS_PER_BATCH = promptscript_v1_default.limits.maxEvents;
|
|
50996
|
+
var MAX_EVENT_COUNT = promptscript_v1_default.limits.maxCount;
|
|
50997
|
+
var MAX_DURATION_SUM_MS = promptscript_v1_default.limits.maxDurationSumMs;
|
|
50998
|
+
var COMMANDS = new Set(promptscript_v1_default.commands);
|
|
50999
|
+
var OUTCOMES = new Set(promptscript_v1_default.outcomes);
|
|
51000
|
+
var FEATURES = new Set(promptscript_v1_default.features);
|
|
51001
|
+
var TARGETS = new Set(promptscript_v1_default.targets);
|
|
51002
|
+
function sanitizeCommand(value) {
|
|
51003
|
+
return COMMANDS.has(value) ? value : "other";
|
|
51004
|
+
}
|
|
51005
|
+
function isTelemetryOutcome(value) {
|
|
51006
|
+
return OUTCOMES.has(value);
|
|
51007
|
+
}
|
|
51008
|
+
function sanitizeFeature(value) {
|
|
51009
|
+
if (FEATURES.has(value)) {
|
|
51010
|
+
return value;
|
|
51011
|
+
}
|
|
51012
|
+
const parts = value.split(":");
|
|
51013
|
+
const target = parts[1];
|
|
51014
|
+
if (parts.length === 2 && parts[0] === "target" && target !== void 0 && TARGETS.has(target)) {
|
|
51015
|
+
return value;
|
|
51016
|
+
}
|
|
51017
|
+
return null;
|
|
51018
|
+
}
|
|
51019
|
+
function isExcludedCommand(value) {
|
|
51020
|
+
return value === "hook" || value === "telemetry" || value === "__telemetry-flush";
|
|
51021
|
+
}
|
|
51022
|
+
|
|
51023
|
+
// packages/telemetry/src/payload.ts
|
|
51024
|
+
function metadataKey(record) {
|
|
51025
|
+
return [record.app_version, record.runtime_version, record.os, record.arch].join(":");
|
|
51026
|
+
}
|
|
51027
|
+
function eventKey(event) {
|
|
51028
|
+
return event.name === "command" ? `${event.name}:${event.command}:${event.outcome}` : `${event.name}:${event.feature}`;
|
|
51029
|
+
}
|
|
51030
|
+
function copyEvent(event) {
|
|
51031
|
+
return event.name === "command" ? { ...event } : { ...event };
|
|
51032
|
+
}
|
|
51033
|
+
function canAggregate(target, source) {
|
|
51034
|
+
if (target.count + source.count > MAX_EVENT_COUNT) {
|
|
51035
|
+
return false;
|
|
51036
|
+
}
|
|
51037
|
+
if (target.name === "command" && source.name === "command") {
|
|
51038
|
+
return target.duration_ms_sum + source.duration_ms_sum <= MAX_DURATION_SUM_MS;
|
|
51039
|
+
}
|
|
51040
|
+
return true;
|
|
51041
|
+
}
|
|
51042
|
+
function addEvent(target, source) {
|
|
51043
|
+
target.count += source.count;
|
|
51044
|
+
if (target.name === "command" && source.name === "command") {
|
|
51045
|
+
target.duration_ms_sum += source.duration_ms_sum;
|
|
51046
|
+
}
|
|
51047
|
+
}
|
|
51048
|
+
function aggregateRecords(records) {
|
|
51049
|
+
const aggregates = [];
|
|
51050
|
+
records.forEach((record, index) => {
|
|
51051
|
+
const key = `${metadataKey(record)}:${eventKey(record.event)}`;
|
|
51052
|
+
const aggregate = aggregates.find(
|
|
51053
|
+
(candidate) => `${metadataKey(candidate.metadata)}:${eventKey(candidate.event)}` === key && canAggregate(candidate.event, record.event)
|
|
51054
|
+
);
|
|
51055
|
+
if (aggregate !== void 0) {
|
|
51056
|
+
addEvent(aggregate.event, record.event);
|
|
51057
|
+
aggregate.sourceIndexes.push(index);
|
|
51058
|
+
return;
|
|
51059
|
+
}
|
|
51060
|
+
aggregates.push({
|
|
51061
|
+
metadata: {
|
|
51062
|
+
app_version: record.app_version,
|
|
51063
|
+
runtime_version: record.runtime_version,
|
|
51064
|
+
os: record.os,
|
|
51065
|
+
arch: record.arch
|
|
51066
|
+
},
|
|
51067
|
+
event: copyEvent(record.event),
|
|
51068
|
+
sourceIndexes: [index]
|
|
51069
|
+
});
|
|
51070
|
+
});
|
|
51071
|
+
return aggregates;
|
|
51072
|
+
}
|
|
51073
|
+
function toPayload(batch) {
|
|
51074
|
+
return {
|
|
51075
|
+
schema: 1,
|
|
51076
|
+
app: "promptscript",
|
|
51077
|
+
event_schema: TELEMETRY_EVENT_SCHEMA,
|
|
51078
|
+
runtime: "node",
|
|
51079
|
+
...batch.metadata,
|
|
51080
|
+
events: batch.events
|
|
51081
|
+
};
|
|
51082
|
+
}
|
|
51083
|
+
function buildTelemetryBatches(records) {
|
|
51084
|
+
const builders = [];
|
|
51085
|
+
for (const aggregate of aggregateRecords(records)) {
|
|
51086
|
+
const key = eventKey(aggregate.event);
|
|
51087
|
+
let batch = builders.find(
|
|
51088
|
+
(candidate) => metadataKey(candidate.metadata) === metadataKey(aggregate.metadata) && candidate.events.length < MAX_EVENTS_PER_BATCH && !candidate.eventKeys.has(key)
|
|
51089
|
+
);
|
|
51090
|
+
if (batch === void 0) {
|
|
51091
|
+
batch = {
|
|
51092
|
+
metadata: aggregate.metadata,
|
|
51093
|
+
events: [],
|
|
51094
|
+
eventKeys: /* @__PURE__ */ new Set(),
|
|
51095
|
+
sourceIndexes: /* @__PURE__ */ new Set()
|
|
51096
|
+
};
|
|
51097
|
+
builders.push(batch);
|
|
51098
|
+
}
|
|
51099
|
+
batch.events.push(aggregate.event);
|
|
51100
|
+
batch.eventKeys.add(key);
|
|
51101
|
+
aggregate.sourceIndexes.forEach((index) => batch?.sourceIndexes.add(index));
|
|
51102
|
+
}
|
|
51103
|
+
return builders.map((batch) => ({
|
|
51104
|
+
payload: toPayload(batch),
|
|
51105
|
+
sourceIndexes: [...batch.sourceIndexes]
|
|
51106
|
+
}));
|
|
51107
|
+
}
|
|
51108
|
+
|
|
51109
|
+
// packages/telemetry/src/spool.ts
|
|
51110
|
+
import {
|
|
51111
|
+
appendFileSync,
|
|
51112
|
+
existsSync as existsSync26,
|
|
51113
|
+
mkdirSync as mkdirSync2,
|
|
51114
|
+
readFileSync as readFileSync7,
|
|
51115
|
+
readdirSync as readdirSync2,
|
|
51116
|
+
renameSync as renameSync2,
|
|
51117
|
+
rmSync,
|
|
51118
|
+
statSync as statSync3,
|
|
51119
|
+
unlinkSync as unlinkSync3,
|
|
51120
|
+
writeFileSync as writeFileSync5
|
|
51121
|
+
} from "node:fs";
|
|
51122
|
+
import { join as join28 } from "node:path";
|
|
51123
|
+
var ACTIVE_FILE = "telemetry.ndjson";
|
|
51124
|
+
var INFLIGHT_FILE = "telemetry.inflight.ndjson";
|
|
51125
|
+
var QUARANTINE_FILE = "telemetry-quarantine.ndjson";
|
|
51126
|
+
var STATE_FILE = "telemetry-state.json";
|
|
51127
|
+
var LOCK_DIRECTORY = "telemetry.lock";
|
|
51128
|
+
var FLUSH_LOCK_DIRECTORY = "telemetry-flush.lock";
|
|
51129
|
+
var SIDECAR_PATTERN = /^telemetry\.\d+\.\d+\.\d+\.ndjson$/;
|
|
51130
|
+
var MAX_SPOOL_BYTES = 64 * 1024;
|
|
51131
|
+
var MAX_SPOOL_RECORDS = 200;
|
|
51132
|
+
var STALE_LOCK_MS = 3e4;
|
|
51133
|
+
var sidecarSequence = 0;
|
|
51134
|
+
function pathFor(cacheDirectory, name) {
|
|
51135
|
+
return join28(cacheDirectory, name);
|
|
51136
|
+
}
|
|
51137
|
+
function isNodeError2(error, code) {
|
|
51138
|
+
return error instanceof Error && "code" in error && error.code === code;
|
|
51139
|
+
}
|
|
51140
|
+
function removeStaleLock(lockPath) {
|
|
51141
|
+
try {
|
|
51142
|
+
if (Date.now() - statSync3(lockPath).mtimeMs > STALE_LOCK_MS) {
|
|
51143
|
+
rmSync(lockPath, { recursive: true });
|
|
51144
|
+
}
|
|
51145
|
+
} catch {
|
|
51146
|
+
}
|
|
51147
|
+
}
|
|
51148
|
+
function acquireLock2(cacheDirectory, lockDirectory = LOCK_DIRECTORY) {
|
|
51149
|
+
mkdirSync2(cacheDirectory, { recursive: true });
|
|
51150
|
+
const lockPath = pathFor(cacheDirectory, lockDirectory);
|
|
51151
|
+
try {
|
|
51152
|
+
mkdirSync2(lockPath);
|
|
51153
|
+
return true;
|
|
51154
|
+
} catch (error) {
|
|
51155
|
+
if (!isNodeError2(error, "EEXIST")) {
|
|
51156
|
+
return false;
|
|
51157
|
+
}
|
|
51158
|
+
removeStaleLock(lockPath);
|
|
51159
|
+
try {
|
|
51160
|
+
mkdirSync2(lockPath);
|
|
51161
|
+
return true;
|
|
51162
|
+
} catch {
|
|
51163
|
+
return false;
|
|
51164
|
+
}
|
|
51165
|
+
}
|
|
51166
|
+
}
|
|
51167
|
+
function releaseLock2(cacheDirectory, lockDirectory = LOCK_DIRECTORY) {
|
|
51168
|
+
try {
|
|
51169
|
+
rmSync(pathFor(cacheDirectory, lockDirectory), { recursive: true });
|
|
51170
|
+
} catch {
|
|
51171
|
+
}
|
|
51172
|
+
}
|
|
51173
|
+
function compactLines(lines) {
|
|
51174
|
+
const selected = [];
|
|
51175
|
+
let bytes = 0;
|
|
51176
|
+
for (let index = lines.length - 1; index >= 0; index -= 1) {
|
|
51177
|
+
const line = lines[index];
|
|
51178
|
+
const lineBytes = Buffer.byteLength(`${line}
|
|
51179
|
+
`);
|
|
51180
|
+
if (selected.length >= MAX_SPOOL_RECORDS || bytes + lineBytes > MAX_SPOOL_BYTES) {
|
|
51181
|
+
break;
|
|
51182
|
+
}
|
|
51183
|
+
selected.push(line);
|
|
51184
|
+
bytes += lineBytes;
|
|
51185
|
+
}
|
|
51186
|
+
return selected.reverse();
|
|
51187
|
+
}
|
|
51188
|
+
function readLines(filePath) {
|
|
51189
|
+
try {
|
|
51190
|
+
return readFileSync7(filePath, "utf8").split("\n").filter(Boolean);
|
|
51191
|
+
} catch {
|
|
51192
|
+
return [];
|
|
51193
|
+
}
|
|
51194
|
+
}
|
|
51195
|
+
function writeLines(filePath, lines) {
|
|
51196
|
+
if (lines.length === 0) {
|
|
51197
|
+
try {
|
|
51198
|
+
unlinkSync3(filePath);
|
|
51199
|
+
} catch (error) {
|
|
51200
|
+
if (!isNodeError2(error, "ENOENT")) {
|
|
51201
|
+
throw error;
|
|
51202
|
+
}
|
|
51203
|
+
}
|
|
51204
|
+
return;
|
|
51205
|
+
}
|
|
51206
|
+
const temporaryPath = `${filePath}.${process.pid}.tmp`;
|
|
51207
|
+
writeFileSync5(temporaryPath, `${lines.join("\n")}
|
|
51208
|
+
`, { encoding: "utf8", mode: 384 });
|
|
51209
|
+
renameSync2(temporaryPath, filePath);
|
|
51210
|
+
}
|
|
51211
|
+
function compactFile(filePath) {
|
|
51212
|
+
writeLines(filePath, compactLines(readLines(filePath)));
|
|
51213
|
+
}
|
|
51214
|
+
function parseTelemetryEvent(value) {
|
|
51215
|
+
if (typeof value !== "object" || value === null || !("name" in value)) {
|
|
51216
|
+
return null;
|
|
51217
|
+
}
|
|
51218
|
+
const event = value;
|
|
51219
|
+
if (!Number.isSafeInteger(event["count"]) || Number(event["count"]) < 1 || Number(event["count"]) > MAX_EVENT_COUNT) {
|
|
51220
|
+
return null;
|
|
51221
|
+
}
|
|
51222
|
+
const count = Number(event["count"]);
|
|
51223
|
+
if (event["name"] === "command") {
|
|
51224
|
+
if (typeof event["command"] === "string" && sanitizeCommand(event["command"]) === event["command"] && typeof event["outcome"] === "string" && isTelemetryOutcome(event["outcome"]) && typeof event["duration_ms_sum"] === "number" && Number.isSafeInteger(event["duration_ms_sum"]) && event["duration_ms_sum"] >= 0 && event["duration_ms_sum"] <= MAX_DURATION_SUM_MS) {
|
|
51225
|
+
return {
|
|
51226
|
+
name: "command",
|
|
51227
|
+
command: event["command"],
|
|
51228
|
+
outcome: event["outcome"],
|
|
51229
|
+
count,
|
|
51230
|
+
duration_ms_sum: event["duration_ms_sum"]
|
|
51231
|
+
};
|
|
51232
|
+
}
|
|
51233
|
+
return null;
|
|
51234
|
+
}
|
|
51235
|
+
if (event["name"] === "feature" && typeof event["feature"] === "string") {
|
|
51236
|
+
const feature = sanitizeFeature(event["feature"]);
|
|
51237
|
+
return feature === null ? null : { name: "feature", feature, count };
|
|
51238
|
+
}
|
|
51239
|
+
return null;
|
|
51240
|
+
}
|
|
51241
|
+
function parseRecord(line) {
|
|
51242
|
+
try {
|
|
51243
|
+
const value = JSON.parse(line);
|
|
51244
|
+
if (typeof value !== "object" || value === null || !("event" in value)) {
|
|
51245
|
+
return null;
|
|
51246
|
+
}
|
|
51247
|
+
const record = value;
|
|
51248
|
+
const event = parseTelemetryEvent(record["event"]);
|
|
51249
|
+
if (typeof record["app_version"] !== "string" || !/^\d{1,5}\.\d{1,5}\.\d{1,5}(?:[-+][0-9A-Za-z.-]{1,32})?$/.test(record["app_version"]) || typeof record["runtime_version"] !== "string" || !/^\d{1,3}$/.test(record["runtime_version"]) || !["darwin", "linux", "windows", "other"].includes(String(record["os"])) || !["arm64", "x86_64", "other"].includes(String(record["arch"])) || event === null) {
|
|
51250
|
+
return null;
|
|
51251
|
+
}
|
|
51252
|
+
return {
|
|
51253
|
+
app_version: record["app_version"],
|
|
51254
|
+
runtime_version: record["runtime_version"],
|
|
51255
|
+
os: record["os"],
|
|
51256
|
+
arch: record["arch"],
|
|
51257
|
+
event
|
|
51258
|
+
};
|
|
51259
|
+
} catch {
|
|
51260
|
+
return null;
|
|
51261
|
+
}
|
|
51262
|
+
}
|
|
51263
|
+
function parseLines(lines) {
|
|
51264
|
+
const records = [];
|
|
51265
|
+
let corruptLines = 0;
|
|
51266
|
+
for (const line of lines) {
|
|
51267
|
+
const record = parseRecord(line);
|
|
51268
|
+
if (record === null) {
|
|
51269
|
+
corruptLines += 1;
|
|
51270
|
+
} else {
|
|
51271
|
+
records.push(record);
|
|
51272
|
+
}
|
|
51273
|
+
}
|
|
51274
|
+
return { records, corruptLines };
|
|
51275
|
+
}
|
|
51276
|
+
function sidecarFiles(cacheDirectory) {
|
|
51277
|
+
try {
|
|
51278
|
+
return readdirSync2(cacheDirectory).filter((name) => SIDECAR_PATTERN.test(name)).map((name) => pathFor(cacheDirectory, name));
|
|
51279
|
+
} catch {
|
|
51280
|
+
return [];
|
|
51281
|
+
}
|
|
51282
|
+
}
|
|
51283
|
+
function mergeSidecars(cacheDirectory) {
|
|
51284
|
+
const activePath = pathFor(cacheDirectory, ACTIVE_FILE);
|
|
51285
|
+
for (const sidecarPath of sidecarFiles(cacheDirectory)) {
|
|
51286
|
+
for (const line of readLines(sidecarPath)) {
|
|
51287
|
+
appendFileSync(activePath, `${line}
|
|
51288
|
+
`, { encoding: "utf8", mode: 384 });
|
|
51289
|
+
}
|
|
51290
|
+
try {
|
|
51291
|
+
unlinkSync3(sidecarPath);
|
|
51292
|
+
} catch {
|
|
51293
|
+
}
|
|
51294
|
+
}
|
|
51295
|
+
compactFile(activePath);
|
|
51296
|
+
}
|
|
51297
|
+
function appendSpoolRecords(cacheDirectory, records) {
|
|
51298
|
+
if (records.length === 0) {
|
|
51299
|
+
return;
|
|
51300
|
+
}
|
|
51301
|
+
const lines = records.map((record) => JSON.stringify(record));
|
|
51302
|
+
try {
|
|
51303
|
+
if (acquireLock2(cacheDirectory)) {
|
|
51304
|
+
try {
|
|
51305
|
+
const activePath = pathFor(cacheDirectory, ACTIVE_FILE);
|
|
51306
|
+
appendFileSync(activePath, `${lines.join("\n")}
|
|
51307
|
+
`, {
|
|
51308
|
+
encoding: "utf8",
|
|
51309
|
+
mode: 384
|
|
51310
|
+
});
|
|
51311
|
+
compactFile(activePath);
|
|
51312
|
+
} finally {
|
|
51313
|
+
releaseLock2(cacheDirectory);
|
|
51314
|
+
}
|
|
51315
|
+
return;
|
|
51316
|
+
}
|
|
51317
|
+
mkdirSync2(cacheDirectory, { recursive: true });
|
|
51318
|
+
sidecarSequence += 1;
|
|
51319
|
+
const sidecarPath = pathFor(
|
|
51320
|
+
cacheDirectory,
|
|
51321
|
+
`telemetry.${process.pid}.${Date.now()}.${sidecarSequence}.ndjson`
|
|
51322
|
+
);
|
|
51323
|
+
const temporaryPath = `${sidecarPath}.tmp`;
|
|
51324
|
+
writeFileSync5(temporaryPath, `${lines.join("\n")}
|
|
51325
|
+
`, { encoding: "utf8", mode: 384 });
|
|
51326
|
+
renameSync2(temporaryPath, sidecarPath);
|
|
51327
|
+
} catch {
|
|
51328
|
+
}
|
|
51329
|
+
}
|
|
51330
|
+
function claimSpool(cacheDirectory) {
|
|
51331
|
+
if (!acquireLock2(cacheDirectory)) {
|
|
51332
|
+
return null;
|
|
51333
|
+
}
|
|
51334
|
+
try {
|
|
51335
|
+
const inflightPath = pathFor(cacheDirectory, INFLIGHT_FILE);
|
|
51336
|
+
if (existsSync26(inflightPath) && readLines(inflightPath).length === 0) {
|
|
51337
|
+
try {
|
|
51338
|
+
unlinkSync3(inflightPath);
|
|
51339
|
+
} catch {
|
|
51340
|
+
return null;
|
|
51341
|
+
}
|
|
51342
|
+
}
|
|
51343
|
+
if (!existsSync26(inflightPath)) {
|
|
51344
|
+
mergeSidecars(cacheDirectory);
|
|
51345
|
+
const activePath = pathFor(cacheDirectory, ACTIVE_FILE);
|
|
51346
|
+
if (existsSync26(activePath)) {
|
|
51347
|
+
renameSync2(activePath, inflightPath);
|
|
51348
|
+
}
|
|
51349
|
+
}
|
|
51350
|
+
const lines = readLines(inflightPath);
|
|
51351
|
+
return lines.length === 0 ? null : parseLines(lines);
|
|
51352
|
+
} finally {
|
|
51353
|
+
releaseLock2(cacheDirectory);
|
|
51354
|
+
}
|
|
51355
|
+
}
|
|
51356
|
+
function completeSpoolClaim(cacheDirectory, remainingRecords, rejectedRecords = []) {
|
|
51357
|
+
if (!acquireLock2(cacheDirectory)) {
|
|
51358
|
+
return false;
|
|
51359
|
+
}
|
|
51360
|
+
try {
|
|
51361
|
+
try {
|
|
51362
|
+
writeLines(
|
|
51363
|
+
pathFor(cacheDirectory, INFLIGHT_FILE),
|
|
51364
|
+
remainingRecords.map((record) => JSON.stringify(record))
|
|
51365
|
+
);
|
|
51366
|
+
if (rejectedRecords.length > 0) {
|
|
51367
|
+
const quarantinePath = pathFor(cacheDirectory, QUARANTINE_FILE);
|
|
51368
|
+
for (const record of rejectedRecords) {
|
|
51369
|
+
appendFileSync(quarantinePath, `${JSON.stringify(record)}
|
|
51370
|
+
`, {
|
|
51371
|
+
encoding: "utf8",
|
|
51372
|
+
mode: 384
|
|
51373
|
+
});
|
|
51374
|
+
}
|
|
51375
|
+
compactFile(quarantinePath);
|
|
51376
|
+
}
|
|
51377
|
+
return true;
|
|
51378
|
+
} catch {
|
|
51379
|
+
return false;
|
|
51380
|
+
}
|
|
51381
|
+
} finally {
|
|
51382
|
+
releaseLock2(cacheDirectory);
|
|
51383
|
+
}
|
|
51384
|
+
}
|
|
51385
|
+
function prepareSpoolAttempt(cacheDirectory, remainingRecords) {
|
|
51386
|
+
if (!acquireLock2(cacheDirectory)) {
|
|
51387
|
+
return false;
|
|
51388
|
+
}
|
|
51389
|
+
try {
|
|
51390
|
+
try {
|
|
51391
|
+
writeLines(
|
|
51392
|
+
pathFor(cacheDirectory, INFLIGHT_FILE),
|
|
51393
|
+
remainingRecords.map((record) => JSON.stringify(record))
|
|
51394
|
+
);
|
|
51395
|
+
return true;
|
|
51396
|
+
} catch {
|
|
51397
|
+
return false;
|
|
51398
|
+
}
|
|
51399
|
+
} finally {
|
|
51400
|
+
releaseLock2(cacheDirectory);
|
|
51401
|
+
}
|
|
51402
|
+
}
|
|
51403
|
+
function acquireFlushLease(cacheDirectory) {
|
|
51404
|
+
return acquireLock2(cacheDirectory, FLUSH_LOCK_DIRECTORY);
|
|
51405
|
+
}
|
|
51406
|
+
function releaseFlushLease(cacheDirectory) {
|
|
51407
|
+
releaseLock2(cacheDirectory, FLUSH_LOCK_DIRECTORY);
|
|
51408
|
+
}
|
|
51409
|
+
function getSpoolInfo(cacheDirectory) {
|
|
51410
|
+
const files = [
|
|
51411
|
+
pathFor(cacheDirectory, ACTIVE_FILE),
|
|
51412
|
+
pathFor(cacheDirectory, INFLIGHT_FILE),
|
|
51413
|
+
...sidecarFiles(cacheDirectory)
|
|
51414
|
+
];
|
|
51415
|
+
let records = 0;
|
|
51416
|
+
let bytes = 0;
|
|
51417
|
+
for (const file of files) {
|
|
51418
|
+
const lines = readLines(file);
|
|
51419
|
+
records += lines.length;
|
|
51420
|
+
try {
|
|
51421
|
+
bytes += statSync3(file).size;
|
|
51422
|
+
} catch {
|
|
51423
|
+
}
|
|
51424
|
+
}
|
|
51425
|
+
return { records, bytes };
|
|
51426
|
+
}
|
|
51427
|
+
function readFlushState(cacheDirectory) {
|
|
51428
|
+
try {
|
|
51429
|
+
const value = JSON.parse(readFileSync7(pathFor(cacheDirectory, STATE_FILE), "utf8"));
|
|
51430
|
+
return typeof value === "object" && value !== null ? value : {};
|
|
51431
|
+
} catch {
|
|
51432
|
+
return {};
|
|
51433
|
+
}
|
|
51434
|
+
}
|
|
51435
|
+
function writeFlushState(cacheDirectory, state) {
|
|
51436
|
+
try {
|
|
51437
|
+
mkdirSync2(cacheDirectory, { recursive: true });
|
|
51438
|
+
const statePath = pathFor(cacheDirectory, STATE_FILE);
|
|
51439
|
+
const temporaryPath = `${statePath}.${process.pid}.tmp`;
|
|
51440
|
+
writeFileSync5(temporaryPath, `${JSON.stringify(state, null, 2)}
|
|
51441
|
+
`, {
|
|
51442
|
+
encoding: "utf8",
|
|
51443
|
+
mode: 384
|
|
51444
|
+
});
|
|
51445
|
+
renameSync2(temporaryPath, statePath);
|
|
51446
|
+
} catch {
|
|
51447
|
+
}
|
|
51448
|
+
}
|
|
51449
|
+
|
|
51450
|
+
// packages/telemetry/src/transport.ts
|
|
51451
|
+
function isSecureEndpoint2(endpoint) {
|
|
51452
|
+
try {
|
|
51453
|
+
const url = new URL(endpoint);
|
|
51454
|
+
return url.protocol === "https:" && url.username === "" && url.password === "";
|
|
51455
|
+
} catch {
|
|
51456
|
+
return false;
|
|
51457
|
+
}
|
|
51458
|
+
}
|
|
51459
|
+
async function postTelemetry(endpoint, payload, timeoutMs, fetchImplementation = fetch) {
|
|
51460
|
+
if (!isSecureEndpoint2(endpoint)) {
|
|
51461
|
+
return "rejected";
|
|
51462
|
+
}
|
|
51463
|
+
const controller = new AbortController();
|
|
51464
|
+
const timeout = setTimeout(
|
|
51465
|
+
() => {
|
|
51466
|
+
controller.abort();
|
|
51467
|
+
},
|
|
51468
|
+
Math.max(0, timeoutMs)
|
|
51469
|
+
);
|
|
51470
|
+
try {
|
|
51471
|
+
const response = await fetchImplementation(endpoint, {
|
|
51472
|
+
method: "POST",
|
|
51473
|
+
redirect: "manual",
|
|
51474
|
+
signal: controller.signal,
|
|
51475
|
+
headers: {
|
|
51476
|
+
"Content-Type": "application/json",
|
|
51477
|
+
"User-Agent": "promptscript-telemetry/1"
|
|
51478
|
+
},
|
|
51479
|
+
body: JSON.stringify(payload)
|
|
51480
|
+
});
|
|
51481
|
+
if (response.ok) {
|
|
51482
|
+
return "sent";
|
|
51483
|
+
}
|
|
51484
|
+
if (response.status >= 300 && response.status < 400) {
|
|
51485
|
+
return "rejected";
|
|
51486
|
+
}
|
|
51487
|
+
if (response.status === 429 || response.status >= 500) {
|
|
51488
|
+
return "retryable";
|
|
51489
|
+
}
|
|
51490
|
+
if (response.status >= 400 && response.status < 500) {
|
|
51491
|
+
return "rejected";
|
|
51492
|
+
}
|
|
51493
|
+
return "retryable";
|
|
51494
|
+
} catch {
|
|
51495
|
+
return "unknown";
|
|
51496
|
+
} finally {
|
|
51497
|
+
clearTimeout(timeout);
|
|
51498
|
+
}
|
|
51499
|
+
}
|
|
51500
|
+
|
|
51501
|
+
// packages/telemetry/src/flush.ts
|
|
51502
|
+
var FLUSH_BUDGET_MS = 5e3;
|
|
51503
|
+
var MAX_BATCHES_PER_FLUSH = 3;
|
|
51504
|
+
var COMPLETION_RETRIES = 20;
|
|
51505
|
+
function delay(milliseconds) {
|
|
51506
|
+
return new Promise((resolve38) => setTimeout(resolve38, milliseconds));
|
|
51507
|
+
}
|
|
51508
|
+
async function persistCompletion(cacheDirectory, remainingRecords, rejectedRecords) {
|
|
51509
|
+
for (let attempt = 0; attempt < COMPLETION_RETRIES; attempt += 1) {
|
|
51510
|
+
if (completeSpoolClaim(cacheDirectory, remainingRecords, rejectedRecords)) {
|
|
51511
|
+
return true;
|
|
51512
|
+
}
|
|
51513
|
+
await delay(10);
|
|
51514
|
+
}
|
|
51515
|
+
return false;
|
|
51516
|
+
}
|
|
51517
|
+
async function persistAttempt(cacheDirectory, remainingRecords) {
|
|
51518
|
+
for (let attempt = 0; attempt < COMPLETION_RETRIES; attempt += 1) {
|
|
51519
|
+
if (prepareSpoolAttempt(cacheDirectory, remainingRecords)) {
|
|
51520
|
+
return true;
|
|
51521
|
+
}
|
|
51522
|
+
await delay(10);
|
|
51523
|
+
}
|
|
51524
|
+
return false;
|
|
51525
|
+
}
|
|
51526
|
+
async function runFlush(config, fetchImplementation = fetch) {
|
|
51527
|
+
const emptyResult = {
|
|
51528
|
+
attempted: false,
|
|
51529
|
+
deliveredRecords: 0,
|
|
51530
|
+
rejectedRecords: 0,
|
|
51531
|
+
remainingRecords: 0
|
|
51532
|
+
};
|
|
51533
|
+
if (!config.enabled || !acquireFlushLease(config.cacheDirectory)) {
|
|
51534
|
+
return emptyResult;
|
|
51535
|
+
}
|
|
51536
|
+
try {
|
|
51537
|
+
const claim = claimSpool(config.cacheDirectory);
|
|
51538
|
+
if (claim === null) {
|
|
51539
|
+
return emptyResult;
|
|
51540
|
+
}
|
|
51541
|
+
const state = readFlushState(config.cacheDirectory);
|
|
51542
|
+
const startedAt = Date.now();
|
|
51543
|
+
writeFlushState(config.cacheDirectory, {
|
|
51544
|
+
...state,
|
|
51545
|
+
lastAttempt: new Date(startedAt).toISOString(),
|
|
51546
|
+
...claim.corruptLines > 0 ? { lastError: `Skipped ${claim.corruptLines} corrupt spool record(s)` } : {}
|
|
51547
|
+
});
|
|
51548
|
+
const remainingIndexes = new Set(claim.records.map((_record, index) => index));
|
|
51549
|
+
const rejectedIndexes = /* @__PURE__ */ new Set();
|
|
51550
|
+
const batches = buildTelemetryBatches(claim.records).slice(0, MAX_BATCHES_PER_FLUSH);
|
|
51551
|
+
let deliveredRecords = 0;
|
|
51552
|
+
let stoppedForRetry = false;
|
|
51553
|
+
for (const batch of batches) {
|
|
51554
|
+
let remainingBudget = FLUSH_BUDGET_MS - (Date.now() - startedAt);
|
|
51555
|
+
if (remainingBudget <= 0) {
|
|
51556
|
+
stoppedForRetry = true;
|
|
51557
|
+
break;
|
|
51558
|
+
}
|
|
51559
|
+
const pendingRecords = claim.records.filter(
|
|
51560
|
+
(_record, index) => remainingIndexes.has(index) && !batch.sourceIndexes.includes(index)
|
|
51561
|
+
);
|
|
51562
|
+
if (!await persistAttempt(config.cacheDirectory, pendingRecords)) {
|
|
51563
|
+
stoppedForRetry = true;
|
|
51564
|
+
break;
|
|
51565
|
+
}
|
|
51566
|
+
for (const index of batch.sourceIndexes) {
|
|
51567
|
+
remainingIndexes.delete(index);
|
|
51568
|
+
}
|
|
51569
|
+
remainingBudget = FLUSH_BUDGET_MS - (Date.now() - startedAt);
|
|
51570
|
+
if (remainingBudget <= 0) {
|
|
51571
|
+
for (const index of batch.sourceIndexes) {
|
|
51572
|
+
remainingIndexes.add(index);
|
|
51573
|
+
}
|
|
51574
|
+
stoppedForRetry = true;
|
|
51575
|
+
break;
|
|
51576
|
+
}
|
|
51577
|
+
const result = await postTelemetry(
|
|
51578
|
+
config.endpoint,
|
|
51579
|
+
batch.payload,
|
|
51580
|
+
remainingBudget,
|
|
51581
|
+
fetchImplementation
|
|
51582
|
+
);
|
|
51583
|
+
if (result === "sent" || result === "unknown") {
|
|
51584
|
+
deliveredRecords += batch.sourceIndexes.length;
|
|
51585
|
+
if (result === "unknown") {
|
|
51586
|
+
break;
|
|
51587
|
+
}
|
|
51588
|
+
continue;
|
|
51589
|
+
}
|
|
51590
|
+
if (result === "rejected") {
|
|
51591
|
+
for (const index of batch.sourceIndexes) {
|
|
51592
|
+
rejectedIndexes.add(index);
|
|
51593
|
+
}
|
|
51594
|
+
continue;
|
|
51595
|
+
}
|
|
51596
|
+
for (const index of batch.sourceIndexes) {
|
|
51597
|
+
remainingIndexes.add(index);
|
|
51598
|
+
}
|
|
51599
|
+
stoppedForRetry = true;
|
|
51600
|
+
break;
|
|
51601
|
+
}
|
|
51602
|
+
const remainingRecords = claim.records.filter((_record, index) => remainingIndexes.has(index));
|
|
51603
|
+
const rejectedRecords = claim.records.filter((_record, index) => rejectedIndexes.has(index));
|
|
51604
|
+
const persisted = await persistCompletion(
|
|
51605
|
+
config.cacheDirectory,
|
|
51606
|
+
remainingRecords,
|
|
51607
|
+
rejectedRecords
|
|
51608
|
+
);
|
|
51609
|
+
const finishedAt = (/* @__PURE__ */ new Date()).toISOString();
|
|
51610
|
+
const lastError = stoppedForRetry ? "Delivery deferred after retryable failure" : rejectedRecords.length > 0 ? `Collector rejected ${rejectedRecords.length} record(s)` : !persisted ? "Could not finalize telemetry spool" : claim.corruptLines > 0 ? `Skipped ${claim.corruptLines} corrupt spool record(s)` : void 0;
|
|
51611
|
+
writeFlushState(config.cacheDirectory, {
|
|
51612
|
+
...readFlushState(config.cacheDirectory),
|
|
51613
|
+
...remainingRecords.length === 0 && persisted ? { lastSuccess: finishedAt } : {},
|
|
51614
|
+
lastError
|
|
51615
|
+
});
|
|
51616
|
+
return {
|
|
51617
|
+
attempted: batches.length > 0,
|
|
51618
|
+
deliveredRecords,
|
|
51619
|
+
rejectedRecords: rejectedRecords.length,
|
|
51620
|
+
remainingRecords: remainingRecords.length
|
|
51621
|
+
};
|
|
51622
|
+
} finally {
|
|
51623
|
+
releaseFlushLease(config.cacheDirectory);
|
|
51624
|
+
}
|
|
51625
|
+
}
|
|
51626
|
+
|
|
51627
|
+
// packages/telemetry/src/reporter.ts
|
|
51628
|
+
import { spawn as spawn4 } from "node:child_process";
|
|
51629
|
+
import { performance } from "node:perf_hooks";
|
|
51630
|
+
var FLUSH_INTERVAL_MS = 4 * 60 * 60 * 1e3;
|
|
51631
|
+
var FLUSH_RECORD_THRESHOLD = 50;
|
|
51632
|
+
var TelemetrySession = class {
|
|
51633
|
+
config;
|
|
51634
|
+
metadata;
|
|
51635
|
+
command;
|
|
51636
|
+
features;
|
|
51637
|
+
startTime;
|
|
51638
|
+
excluded;
|
|
51639
|
+
finished = false;
|
|
51640
|
+
constructor(options) {
|
|
51641
|
+
this.config = options.config;
|
|
51642
|
+
this.metadata = options.metadata;
|
|
51643
|
+
this.excluded = isExcludedCommand(options.command);
|
|
51644
|
+
this.command = sanitizeCommand(options.command);
|
|
51645
|
+
this.features = (options.features ?? []).map((feature) => sanitizeFeature(feature)).filter((feature) => feature !== null);
|
|
51646
|
+
this.startTime = options.startTime ?? performance.now();
|
|
51647
|
+
}
|
|
51648
|
+
finish(outcome) {
|
|
51649
|
+
if (this.finished || !this.config.enabled || this.excluded) {
|
|
51650
|
+
return;
|
|
51651
|
+
}
|
|
51652
|
+
this.finished = true;
|
|
51653
|
+
const duration = Math.max(0, Math.round(performance.now() - this.startTime));
|
|
51654
|
+
const records = [
|
|
51655
|
+
{
|
|
51656
|
+
...this.metadata,
|
|
51657
|
+
event: {
|
|
51658
|
+
name: "command",
|
|
51659
|
+
command: this.command,
|
|
51660
|
+
outcome,
|
|
51661
|
+
count: 1,
|
|
51662
|
+
duration_ms_sum: duration
|
|
51663
|
+
}
|
|
51664
|
+
},
|
|
51665
|
+
...this.features.map((feature) => ({
|
|
51666
|
+
...this.metadata,
|
|
51667
|
+
event: { name: "feature", feature, count: 1 }
|
|
51668
|
+
}))
|
|
51669
|
+
];
|
|
51670
|
+
appendSpoolRecords(this.config.cacheDirectory, records);
|
|
51671
|
+
}
|
|
51672
|
+
};
|
|
51673
|
+
function shouldFlush(config, now) {
|
|
51674
|
+
const spool = getSpoolInfo(config.cacheDirectory);
|
|
51675
|
+
if (spool.records === 0) {
|
|
51676
|
+
return false;
|
|
51677
|
+
}
|
|
51678
|
+
if (spool.records >= FLUSH_RECORD_THRESHOLD) {
|
|
51679
|
+
return true;
|
|
51680
|
+
}
|
|
51681
|
+
const lastAttempt = readFlushState(config.cacheDirectory).lastAttempt;
|
|
51682
|
+
if (lastAttempt === void 0) {
|
|
51683
|
+
return true;
|
|
51684
|
+
}
|
|
51685
|
+
const timestamp = Date.parse(lastAttempt);
|
|
51686
|
+
return !Number.isFinite(timestamp) || now - timestamp >= FLUSH_INTERVAL_MS;
|
|
51687
|
+
}
|
|
51688
|
+
function maybeSpawnFlush(config, options = {}) {
|
|
51689
|
+
const environment = options.environment ?? process.env;
|
|
51690
|
+
const entrypoint = options.entrypoint ?? process.argv[1];
|
|
51691
|
+
if (!config.enabled || environment["PROMPTSCRIPT_TELEMETRY_FLUSH"] === "1" || entrypoint === void 0 || !shouldFlush(config, options.now ?? Date.now())) {
|
|
51692
|
+
return false;
|
|
51693
|
+
}
|
|
51694
|
+
try {
|
|
51695
|
+
const child = (options.spawn ?? spawn4)(
|
|
51696
|
+
options.executable ?? process.execPath,
|
|
51697
|
+
[entrypoint, "__telemetry-flush"],
|
|
51698
|
+
{
|
|
51699
|
+
detached: true,
|
|
51700
|
+
stdio: "ignore",
|
|
51701
|
+
env: {
|
|
51702
|
+
...environment,
|
|
51703
|
+
PROMPTSCRIPT_TELEMETRY_FLUSH: "1",
|
|
51704
|
+
PROMPTSCRIPT_NO_UPDATE_CHECK: "1"
|
|
51705
|
+
}
|
|
51706
|
+
}
|
|
51707
|
+
);
|
|
51708
|
+
child.unref();
|
|
51709
|
+
return true;
|
|
51710
|
+
} catch {
|
|
51711
|
+
return false;
|
|
51712
|
+
}
|
|
51713
|
+
}
|
|
51714
|
+
|
|
51715
|
+
// packages/telemetry/src/runtime.ts
|
|
51716
|
+
function operatingSystem(platform) {
|
|
51717
|
+
switch (platform) {
|
|
51718
|
+
case "darwin":
|
|
51719
|
+
return "darwin";
|
|
51720
|
+
case "linux":
|
|
51721
|
+
return "linux";
|
|
51722
|
+
case "win32":
|
|
51723
|
+
return "windows";
|
|
51724
|
+
default:
|
|
51725
|
+
return "other";
|
|
51726
|
+
}
|
|
51727
|
+
}
|
|
51728
|
+
function architecture(value) {
|
|
51729
|
+
switch (value) {
|
|
51730
|
+
case "arm64":
|
|
51731
|
+
return "arm64";
|
|
51732
|
+
case "x64":
|
|
51733
|
+
return "x86_64";
|
|
51734
|
+
default:
|
|
51735
|
+
return "other";
|
|
51736
|
+
}
|
|
51737
|
+
}
|
|
51738
|
+
function runtimeMetadata(appVersion, input2 = {}) {
|
|
51739
|
+
return {
|
|
51740
|
+
app_version: appVersion,
|
|
51741
|
+
runtime_version: (input2.nodeVersion ?? process.versions.node).split(".")[0] ?? "0",
|
|
51742
|
+
os: operatingSystem(input2.platform ?? process.platform),
|
|
51743
|
+
arch: architecture(input2.architecture ?? process.arch)
|
|
51744
|
+
};
|
|
51745
|
+
}
|
|
51746
|
+
|
|
51747
|
+
// packages/cli/src/telemetry/config.ts
|
|
51748
|
+
init_loader2();
|
|
51749
|
+
init_user_config();
|
|
51750
|
+
function resolveFrom(baseDirectory, value) {
|
|
51751
|
+
return isAbsolute15(value) ? value : resolve34(baseDirectory, value);
|
|
51752
|
+
}
|
|
51753
|
+
function findProjectConfig(cwd, customPath) {
|
|
51754
|
+
if (customPath !== void 0) {
|
|
51755
|
+
const path2 = resolveFrom(cwd, customPath);
|
|
51756
|
+
return existsSync27(path2) ? path2 : null;
|
|
51757
|
+
}
|
|
51758
|
+
const environmentPath = process.env["PROMPTSCRIPT_CONFIG"];
|
|
51759
|
+
if (environmentPath !== void 0 && environmentPath !== "") {
|
|
51760
|
+
const path2 = resolveFrom(cwd, environmentPath);
|
|
51761
|
+
return existsSync27(path2) ? path2 : null;
|
|
51762
|
+
}
|
|
51763
|
+
for (const file of CONFIG_FILES) {
|
|
51764
|
+
const path2 = join29(cwd, file);
|
|
51765
|
+
if (existsSync27(path2)) {
|
|
51766
|
+
return path2;
|
|
51767
|
+
}
|
|
51768
|
+
}
|
|
51769
|
+
return null;
|
|
51770
|
+
}
|
|
51771
|
+
function isNodeError3(error, code) {
|
|
51772
|
+
return error instanceof Error && "code" in error && error.code === code;
|
|
51773
|
+
}
|
|
51774
|
+
async function readUserTelemetrySetting(configPath) {
|
|
51775
|
+
let source;
|
|
51776
|
+
try {
|
|
51777
|
+
source = await readFile27(configPath, "utf8");
|
|
51778
|
+
} catch (error) {
|
|
51779
|
+
return isNodeError3(error, "ENOENT") ? { valid: true } : { valid: false };
|
|
51780
|
+
}
|
|
51781
|
+
try {
|
|
51782
|
+
const document = parseDocument(source);
|
|
51783
|
+
if (document.errors.length > 0) {
|
|
51784
|
+
return { valid: false };
|
|
51785
|
+
}
|
|
51786
|
+
const value = document.get("telemetry");
|
|
51787
|
+
if (value === void 0) {
|
|
51788
|
+
return { valid: true };
|
|
51789
|
+
}
|
|
51790
|
+
return typeof value === "boolean" ? { enabled: value, valid: true } : { valid: false };
|
|
51791
|
+
} catch {
|
|
51792
|
+
return { valid: false };
|
|
51793
|
+
}
|
|
51794
|
+
}
|
|
51795
|
+
async function resolveCliTelemetryConfig(options = {}) {
|
|
51796
|
+
const cwd = options.cwd === void 0 ? process.cwd() : resolve34(process.cwd(), options.cwd);
|
|
51797
|
+
const userSetting = await readUserTelemetrySetting(options.userConfigPath ?? USER_CONFIG_PATH);
|
|
51798
|
+
const projectPath = findProjectConfig(cwd, options.config);
|
|
51799
|
+
const environmentConfig = process.env["PROMPTSCRIPT_CONFIG"];
|
|
51800
|
+
const projectConfigRequested = options.config !== void 0 || environmentConfig !== void 0 && environmentConfig !== "";
|
|
51801
|
+
let projectEnabled;
|
|
51802
|
+
let configurationValid = userSetting.valid;
|
|
51803
|
+
if (projectConfigRequested && projectPath === null) {
|
|
51804
|
+
configurationValid = false;
|
|
51805
|
+
} else if (projectPath !== null) {
|
|
51806
|
+
try {
|
|
51807
|
+
const value = (await loadConfig(projectPath)).telemetry;
|
|
51808
|
+
if (value === void 0 || typeof value === "boolean") {
|
|
51809
|
+
projectEnabled = value;
|
|
51810
|
+
} else {
|
|
51811
|
+
configurationValid = false;
|
|
51812
|
+
}
|
|
51813
|
+
} catch {
|
|
51814
|
+
configurationValid = false;
|
|
51815
|
+
}
|
|
51816
|
+
}
|
|
51817
|
+
return resolveTelemetryConfig({
|
|
51818
|
+
environment: process.env,
|
|
51819
|
+
userEnabled: userSetting.enabled,
|
|
51820
|
+
projectEnabled,
|
|
51821
|
+
configurationValid,
|
|
51822
|
+
...options.cacheDirectory === void 0 ? {} : { cacheDirectory: options.cacheDirectory }
|
|
51823
|
+
});
|
|
51824
|
+
}
|
|
51825
|
+
async function setUserTelemetryEnabled(enabled, configPath = USER_CONFIG_PATH) {
|
|
51826
|
+
let source = "version: '1'\n";
|
|
51827
|
+
try {
|
|
51828
|
+
source = await readFile27(configPath, "utf8");
|
|
51829
|
+
} catch (error) {
|
|
51830
|
+
if (!isNodeError3(error, "ENOENT")) {
|
|
51831
|
+
throw error;
|
|
51832
|
+
}
|
|
51833
|
+
}
|
|
51834
|
+
const document = parseDocument(source);
|
|
51835
|
+
if (document.errors.length > 0) {
|
|
51836
|
+
throw new Error(`Cannot update invalid YAML user config: ${configPath}`);
|
|
51837
|
+
}
|
|
51838
|
+
document.set("telemetry", enabled);
|
|
51839
|
+
await mkdir11(dirname21(configPath), { recursive: true });
|
|
51840
|
+
const temporaryPath = `${configPath}.${process.pid}.tmp`;
|
|
51841
|
+
await writeFile11(temporaryPath, document.toString(), { encoding: "utf8", mode: 384 });
|
|
51842
|
+
await rename2(temporaryPath, configPath);
|
|
51843
|
+
}
|
|
51844
|
+
|
|
51845
|
+
// packages/cli/src/telemetry/session.ts
|
|
51846
|
+
var activeSession = null;
|
|
51847
|
+
var exitHandlerInstalled = false;
|
|
51848
|
+
function normalizedCommandName(command) {
|
|
51849
|
+
const name = command.name();
|
|
51850
|
+
const parentName = command.parent?.name();
|
|
51851
|
+
if (parentName === "vendor" || parentName === "skills") {
|
|
51852
|
+
return `${parentName}-${name}`;
|
|
51853
|
+
}
|
|
51854
|
+
if (parentName === "registry") {
|
|
51855
|
+
return "registry";
|
|
51856
|
+
}
|
|
51857
|
+
return name;
|
|
51858
|
+
}
|
|
51859
|
+
function stringValues(value) {
|
|
51860
|
+
if (typeof value === "string") {
|
|
51861
|
+
return [value];
|
|
51862
|
+
}
|
|
51863
|
+
if (Array.isArray(value)) {
|
|
51864
|
+
return value.filter((item) => typeof item === "string");
|
|
51865
|
+
}
|
|
51866
|
+
return [];
|
|
51867
|
+
}
|
|
51868
|
+
function commandFeatures(command) {
|
|
51869
|
+
const options = command.opts();
|
|
51870
|
+
const features = /* @__PURE__ */ new Set();
|
|
51871
|
+
const targetValues = [...stringValues(options["target"]), ...stringValues(options["targets"])];
|
|
51872
|
+
for (const target of targetValues) {
|
|
51873
|
+
const feature = sanitizeFeature(`target:${target}`);
|
|
51874
|
+
if (feature !== null) {
|
|
51875
|
+
features.add(feature);
|
|
51876
|
+
}
|
|
51877
|
+
}
|
|
51878
|
+
if (options["dryRun"] === true) {
|
|
51879
|
+
features.add("dry_run");
|
|
51880
|
+
}
|
|
51881
|
+
if (options["watch"] === true) {
|
|
51882
|
+
features.add("watch");
|
|
51883
|
+
}
|
|
51884
|
+
if (options["strict"] === true) {
|
|
51885
|
+
features.add("strict");
|
|
51886
|
+
}
|
|
51887
|
+
if (options["build"] !== void 0 || command.name() === "build") {
|
|
51888
|
+
features.add("build_profile");
|
|
51889
|
+
}
|
|
51890
|
+
if (process.env["CI"] !== void 0) {
|
|
51891
|
+
features.add("ci");
|
|
51892
|
+
}
|
|
51893
|
+
return [...features];
|
|
51894
|
+
}
|
|
51895
|
+
function exitOutcome(code) {
|
|
51896
|
+
if (code === 130 || code === 143) {
|
|
51897
|
+
return "cancelled";
|
|
51898
|
+
}
|
|
51899
|
+
return code === 0 ? "success" : "error";
|
|
51900
|
+
}
|
|
51901
|
+
function installExitHandler() {
|
|
51902
|
+
if (exitHandlerInstalled) {
|
|
51903
|
+
return;
|
|
51904
|
+
}
|
|
51905
|
+
process.on("exit", (code) => {
|
|
51906
|
+
finishCliTelemetry(exitOutcome(code));
|
|
51907
|
+
});
|
|
51908
|
+
for (const [signal, fallbackCode] of [
|
|
51909
|
+
["SIGINT", 130],
|
|
51910
|
+
["SIGTERM", 143]
|
|
51911
|
+
]) {
|
|
51912
|
+
process.once(signal, () => {
|
|
51913
|
+
finishCliTelemetry("cancelled");
|
|
51914
|
+
if (process.listenerCount(signal) > 0) {
|
|
51915
|
+
return;
|
|
51916
|
+
}
|
|
51917
|
+
try {
|
|
51918
|
+
process.kill(process.pid, signal);
|
|
51919
|
+
} catch {
|
|
51920
|
+
process.exit(fallbackCode);
|
|
51921
|
+
}
|
|
51922
|
+
});
|
|
51923
|
+
}
|
|
51924
|
+
exitHandlerInstalled = true;
|
|
51925
|
+
}
|
|
51926
|
+
async function prepareCliTelemetry(command, appVersion) {
|
|
51927
|
+
const name = normalizedCommandName(command);
|
|
51928
|
+
if (isExcludedCommand(name) || process.env["PROMPTSCRIPT_TELEMETRY_FLUSH"] === "1") {
|
|
51929
|
+
return;
|
|
51930
|
+
}
|
|
51931
|
+
const options = command.opts();
|
|
51932
|
+
const config = await resolveCliTelemetryConfig({
|
|
51933
|
+
...typeof options["cwd"] === "string" ? { cwd: options["cwd"] } : {},
|
|
51934
|
+
...typeof options["config"] === "string" ? { config: options["config"] } : {}
|
|
51935
|
+
});
|
|
51936
|
+
maybeSpawnFlush(config);
|
|
51937
|
+
activeSession = new TelemetrySession({
|
|
51938
|
+
config,
|
|
51939
|
+
metadata: runtimeMetadata(appVersion),
|
|
51940
|
+
command: name,
|
|
51941
|
+
features: commandFeatures(command)
|
|
51942
|
+
});
|
|
51943
|
+
installExitHandler();
|
|
51944
|
+
}
|
|
51945
|
+
function finishCliTelemetry(outcome) {
|
|
51946
|
+
const session = activeSession;
|
|
51947
|
+
activeSession = null;
|
|
51948
|
+
session?.finish(
|
|
51949
|
+
outcome ?? (process.exitCode === void 0 || process.exitCode === 0 ? "success" : "error")
|
|
51950
|
+
);
|
|
51951
|
+
}
|
|
51952
|
+
async function flushCliTelemetry() {
|
|
51953
|
+
const config = await resolveCliTelemetryConfig();
|
|
51954
|
+
await runFlush(config);
|
|
51955
|
+
}
|
|
51956
|
+
async function telemetryStatus() {
|
|
51957
|
+
const config = await resolveCliTelemetryConfig();
|
|
51958
|
+
return {
|
|
51959
|
+
config,
|
|
51960
|
+
spool: getSpoolInfo(config.cacheDirectory),
|
|
51961
|
+
state: readFlushState(config.cacheDirectory)
|
|
51962
|
+
};
|
|
51963
|
+
}
|
|
51964
|
+
|
|
51965
|
+
// packages/cli/src/commands/telemetry.ts
|
|
51966
|
+
async function telemetryCommand(action) {
|
|
51967
|
+
if (action === "enable") {
|
|
51968
|
+
await setUserTelemetryEnabled(true);
|
|
51969
|
+
ConsoleOutput.success("Anonymous usage telemetry enabled in user config");
|
|
51970
|
+
return;
|
|
51971
|
+
}
|
|
51972
|
+
if (action === "disable") {
|
|
51973
|
+
await setUserTelemetryEnabled(false);
|
|
51974
|
+
ConsoleOutput.success("Anonymous usage telemetry disabled in user config");
|
|
51975
|
+
return;
|
|
51976
|
+
}
|
|
51977
|
+
if (action !== "status") {
|
|
51978
|
+
ConsoleOutput.error("Action must be status, enable, or disable");
|
|
51979
|
+
process.exitCode = 1;
|
|
51980
|
+
return;
|
|
51981
|
+
}
|
|
51982
|
+
const { config, spool, state } = await telemetryStatus();
|
|
51983
|
+
console.log(`Enabled: ${config.enabled ? "yes" : "no"}`);
|
|
51984
|
+
console.log(`Endpoint: ${config.endpoint}`);
|
|
51985
|
+
console.log(`Spool records: ${spool.records}`);
|
|
51986
|
+
console.log(`Spool bytes: ${spool.bytes}`);
|
|
51987
|
+
console.log(`Last attempt: ${state.lastAttempt ?? "never"}`);
|
|
51988
|
+
console.log(`Last success: ${state.lastSuccess ?? "never"}`);
|
|
51989
|
+
console.log(`Last error: ${state.lastError ?? "none"}`);
|
|
51990
|
+
if (config.vetoes.length > 0) {
|
|
51991
|
+
console.log(`Disabled by: ${config.vetoes.join(", ")}`);
|
|
51992
|
+
}
|
|
51993
|
+
}
|
|
51994
|
+
|
|
50744
51995
|
// packages/cli/src/commands/skills.ts
|
|
50745
51996
|
init_src2();
|
|
50746
51997
|
init_loader2();
|
|
50747
51998
|
init_console();
|
|
50748
51999
|
init_src();
|
|
50749
|
-
import { writeFile as
|
|
50750
|
-
import { resolve as
|
|
50751
|
-
import { existsSync as
|
|
52000
|
+
import { writeFile as writeFile12, readFile as readFile28, readdir as readdir9, mkdtemp as mkdtemp2, rm as rm2 } from "fs/promises";
|
|
52001
|
+
import { resolve as resolve35, join as join30, basename as basename17, dirname as dirname22 } from "path";
|
|
52002
|
+
import { existsSync as existsSync28 } from "fs";
|
|
50752
52003
|
import { tmpdir as tmpdir3 } from "os";
|
|
50753
52004
|
import { parse as parseYaml15, stringify as stringifyYaml4 } from "yaml";
|
|
50754
52005
|
var REMOTE_SOURCE_PATTERN = /^[a-zA-Z0-9][\w.-]*\.[a-zA-Z]{2,}\/.+/;
|
|
@@ -50796,32 +52047,32 @@ async function collectProjectRemoteImports(entryFile) {
|
|
|
50796
52047
|
const projectRoot = process.cwd();
|
|
50797
52048
|
const entryFiles = /* @__PURE__ */ new Set();
|
|
50798
52049
|
if (entryFile) {
|
|
50799
|
-
entryFiles.add(
|
|
52050
|
+
entryFiles.add(resolve35(entryFile));
|
|
50800
52051
|
}
|
|
50801
52052
|
if (config) {
|
|
50802
52053
|
if (config.input?.entry) {
|
|
50803
|
-
entryFiles.add(
|
|
52054
|
+
entryFiles.add(resolve35(projectRoot, config.input.entry));
|
|
50804
52055
|
}
|
|
50805
52056
|
for (const build of Object.values(config.builds ?? {})) {
|
|
50806
52057
|
if (build.entry) {
|
|
50807
|
-
entryFiles.add(
|
|
52058
|
+
entryFiles.add(resolve35(projectRoot, build.entry));
|
|
50808
52059
|
}
|
|
50809
52060
|
}
|
|
50810
|
-
const defaultEntry =
|
|
50811
|
-
if (!config.input?.entry && (
|
|
52061
|
+
const defaultEntry = resolve35(projectRoot, ".promptscript/project.prs");
|
|
52062
|
+
if (!config.input?.entry && (existsSync28(defaultEntry) || entryFiles.size === 0)) {
|
|
50812
52063
|
entryFiles.add(defaultEntry);
|
|
50813
52064
|
}
|
|
50814
52065
|
} else if (!entryFile) {
|
|
50815
|
-
const defaultEntry =
|
|
50816
|
-
if (
|
|
52066
|
+
const defaultEntry = resolve35(projectRoot, ".promptscript/project.prs");
|
|
52067
|
+
if (existsSync28(defaultEntry)) {
|
|
50817
52068
|
entryFiles.add(defaultEntry);
|
|
50818
52069
|
} else {
|
|
50819
|
-
const promptscriptDir =
|
|
50820
|
-
if (
|
|
52070
|
+
const promptscriptDir = resolve35(projectRoot, ".promptscript");
|
|
52071
|
+
if (existsSync28(promptscriptDir)) {
|
|
50821
52072
|
const entries = await readdir9(promptscriptDir);
|
|
50822
52073
|
const prsFiles = entries.filter((entry) => entry.endsWith(".prs"));
|
|
50823
52074
|
if (prsFiles.length === 1) {
|
|
50824
|
-
entryFiles.add(
|
|
52075
|
+
entryFiles.add(resolve35(promptscriptDir, prsFiles[0]));
|
|
50825
52076
|
}
|
|
50826
52077
|
}
|
|
50827
52078
|
}
|
|
@@ -50830,7 +52081,7 @@ async function collectProjectRemoteImports(entryFile) {
|
|
|
50830
52081
|
for (const projectEntry of entryFiles) {
|
|
50831
52082
|
imports.push(
|
|
50832
52083
|
...await collectRemoteImports(projectEntry, {
|
|
50833
|
-
localPath:
|
|
52084
|
+
localPath: resolve35(projectRoot, ".promptscript"),
|
|
50834
52085
|
registries: config?.registries,
|
|
50835
52086
|
strict: true,
|
|
50836
52087
|
deduplicate: false,
|
|
@@ -50880,7 +52131,7 @@ function deriveSkillFolderName(source) {
|
|
|
50880
52131
|
const subPath = extractSubPath(source);
|
|
50881
52132
|
if (!subPath) return basename17(parseSkillSource(source).path);
|
|
50882
52133
|
if (!subPath.toLowerCase().endsWith(".md")) return basename17(subPath);
|
|
50883
|
-
const parent =
|
|
52134
|
+
const parent = dirname22(subPath);
|
|
50884
52135
|
if (parent === "." || parent === "") return basename17(parseSkillSource(source).path);
|
|
50885
52136
|
return basename17(parent);
|
|
50886
52137
|
}
|
|
@@ -50897,16 +52148,16 @@ function collectExistingSkillNames(lockfile, exclude) {
|
|
|
50897
52148
|
async function fetchAndValidateRemoteSkill(source, options) {
|
|
50898
52149
|
const skillFilePath = extractSkillFilePath(source);
|
|
50899
52150
|
const repoUrl = options.gitUrl ?? extractRepoUrl(source);
|
|
50900
|
-
const tmp = await mkdtemp2(
|
|
52151
|
+
const tmp = await mkdtemp2(join30(tmpdir3(), "prs-skill-validate-"));
|
|
50901
52152
|
const repoName = basename17(parseSkillSource(source).path.split("/")[2]);
|
|
50902
|
-
const cloneDir = !skillFilePath.includes("/") ?
|
|
52153
|
+
const cloneDir = !skillFilePath.includes("/") ? join30(tmp, repoName) : tmp;
|
|
50903
52154
|
try {
|
|
50904
52155
|
const gitRegistry = createGitRegistry({ url: repoUrl });
|
|
50905
52156
|
const cloneRef = options.version === "latest" || /^[0-9a-f]{40}$/i.test(options.version) ? void 0 : options.version;
|
|
50906
52157
|
await gitRegistry.cloneAtTag(repoUrl, cloneRef, cloneDir);
|
|
50907
52158
|
await gitRegistry.checkoutCommit(cloneDir, options.commit);
|
|
50908
52159
|
const subPath = extractSubPath(source);
|
|
50909
|
-
if (!subPath.toLowerCase().endsWith(".md") &&
|
|
52160
|
+
if (!subPath.toLowerCase().endsWith(".md") && existsSync28(join30(cloneDir, `${subPath}.prs`))) {
|
|
50910
52161
|
return {
|
|
50911
52162
|
content: "",
|
|
50912
52163
|
integrity: "",
|
|
@@ -50920,10 +52171,10 @@ async function fetchAndValidateRemoteSkill(source, options) {
|
|
|
50920
52171
|
]
|
|
50921
52172
|
};
|
|
50922
52173
|
}
|
|
50923
|
-
const filePath =
|
|
52174
|
+
const filePath = join30(cloneDir, skillFilePath);
|
|
50924
52175
|
let content;
|
|
50925
52176
|
try {
|
|
50926
|
-
content = await
|
|
52177
|
+
content = await readFile28(filePath, "utf-8");
|
|
50927
52178
|
} catch {
|
|
50928
52179
|
return {
|
|
50929
52180
|
content: "",
|
|
@@ -50999,8 +52250,8 @@ function validateRemoteSource(source) {
|
|
|
50999
52250
|
}
|
|
51000
52251
|
async function resolveEntryFile(fileFlag) {
|
|
51001
52252
|
if (fileFlag) {
|
|
51002
|
-
const resolved =
|
|
51003
|
-
if (!
|
|
52253
|
+
const resolved = resolve35(fileFlag);
|
|
52254
|
+
if (!existsSync28(resolved)) {
|
|
51004
52255
|
throw new Error(`File not found: ${resolved}`);
|
|
51005
52256
|
}
|
|
51006
52257
|
return resolved;
|
|
@@ -51010,24 +52261,24 @@ async function resolveEntryFile(fileFlag) {
|
|
|
51010
52261
|
try {
|
|
51011
52262
|
const config = await loadConfig();
|
|
51012
52263
|
if (config.input?.entry) {
|
|
51013
|
-
const entryPath =
|
|
51014
|
-
if (
|
|
52264
|
+
const entryPath = resolve35(config.input.entry);
|
|
52265
|
+
if (existsSync28(entryPath)) {
|
|
51015
52266
|
return entryPath;
|
|
51016
52267
|
}
|
|
51017
52268
|
}
|
|
51018
52269
|
} catch {
|
|
51019
52270
|
}
|
|
51020
52271
|
}
|
|
51021
|
-
const promptscriptDir =
|
|
51022
|
-
if (
|
|
52272
|
+
const promptscriptDir = resolve35(".promptscript");
|
|
52273
|
+
if (existsSync28(promptscriptDir)) {
|
|
51023
52274
|
const entries = await readdir9(promptscriptDir);
|
|
51024
52275
|
const prsFiles = entries.filter((f) => f.endsWith(".prs"));
|
|
51025
52276
|
if (prsFiles.length === 1) {
|
|
51026
|
-
return
|
|
52277
|
+
return resolve35(promptscriptDir, prsFiles[0]);
|
|
51027
52278
|
}
|
|
51028
52279
|
if (prsFiles.length > 1) {
|
|
51029
52280
|
if (prsFiles.includes("project.prs")) {
|
|
51030
|
-
return
|
|
52281
|
+
return resolve35(promptscriptDir, "project.prs");
|
|
51031
52282
|
}
|
|
51032
52283
|
throw new Error(
|
|
51033
52284
|
`Multiple .prs files found in .promptscript/. Use --file to specify which one.`
|
|
@@ -51128,12 +52379,12 @@ function findUseEndLine(lines, startLine) {
|
|
|
51128
52379
|
}
|
|
51129
52380
|
async function loadLockfile3() {
|
|
51130
52381
|
const defaultLockfile = { version: LOCKFILE_VERSION, dependencies: {} };
|
|
51131
|
-
if (!
|
|
52382
|
+
if (!existsSync28(LOCKFILE_PATH)) {
|
|
51132
52383
|
return defaultLockfile;
|
|
51133
52384
|
}
|
|
51134
52385
|
let parsed;
|
|
51135
52386
|
try {
|
|
51136
|
-
const raw = await
|
|
52387
|
+
const raw = await readFile28(LOCKFILE_PATH, "utf-8");
|
|
51137
52388
|
parsed = parseYaml15(raw, { maxAliasCount: 100 });
|
|
51138
52389
|
} catch (error) {
|
|
51139
52390
|
throw new Error(
|
|
@@ -51147,7 +52398,7 @@ async function loadLockfile3() {
|
|
|
51147
52398
|
return parsed;
|
|
51148
52399
|
}
|
|
51149
52400
|
async function saveLockfile(lockfile) {
|
|
51150
|
-
await
|
|
52401
|
+
await writeFile12(LOCKFILE_PATH, stringifyYaml4(lockfile), "utf-8");
|
|
51151
52402
|
}
|
|
51152
52403
|
async function skillsAddCommand(rawSource, options) {
|
|
51153
52404
|
const spinner = createSpinner("Adding skill...").start();
|
|
@@ -51170,7 +52421,7 @@ async function skillsAddCommand(rawSource, options) {
|
|
|
51170
52421
|
}
|
|
51171
52422
|
spinner.text = "Resolving entry file...";
|
|
51172
52423
|
const entryFile = await resolveEntryFile(options.file);
|
|
51173
|
-
const content = await
|
|
52424
|
+
const content = await readFile28(entryFile, "utf-8");
|
|
51174
52425
|
const lines = content.split("\n");
|
|
51175
52426
|
const alreadyImported = lines.some((line) => {
|
|
51176
52427
|
const usePath = extractUsePath(line);
|
|
@@ -51282,7 +52533,7 @@ async function skillsAddCommand(rawSource, options) {
|
|
|
51282
52533
|
ConsoleOutput.dryRun(`Would update ${LOCKFILE_PATH}`);
|
|
51283
52534
|
return;
|
|
51284
52535
|
}
|
|
51285
|
-
await
|
|
52536
|
+
await writeFile12(entryFile, updatedContent, "utf-8");
|
|
51286
52537
|
await saveLockfile(lockfile);
|
|
51287
52538
|
spinner.succeed("Skill added");
|
|
51288
52539
|
ConsoleOutput.newline();
|
|
@@ -51298,7 +52549,7 @@ async function skillsRemoveCommand(name, options) {
|
|
|
51298
52549
|
const spinner = createSpinner("Removing skill...").start();
|
|
51299
52550
|
try {
|
|
51300
52551
|
const entryFile = await resolveEntryFile();
|
|
51301
|
-
const content = await
|
|
52552
|
+
const content = await readFile28(entryFile, "utf-8");
|
|
51302
52553
|
const lines = content.split("\n");
|
|
51303
52554
|
const parsed = parse(content, { filename: entryFile, tolerant: true });
|
|
51304
52555
|
if (!parsed.ast || parsed.errors.length > 0) {
|
|
@@ -51378,7 +52629,7 @@ async function skillsRemoveCommand(name, options) {
|
|
|
51378
52629
|
return;
|
|
51379
52630
|
}
|
|
51380
52631
|
lines.splice(lineIndex, candidate.endIndex - lineIndex + 1);
|
|
51381
|
-
await
|
|
52632
|
+
await writeFile12(entryFile, lines.join("\n"), "utf-8");
|
|
51382
52633
|
if (removedPath in lockfile.dependencies && !hasSameImportRemaining) {
|
|
51383
52634
|
delete lockfile.dependencies[removedPath];
|
|
51384
52635
|
}
|
|
@@ -51414,7 +52665,7 @@ async function skillsListCommand() {
|
|
|
51414
52665
|
const spinner = createSpinner("Loading skills...").start();
|
|
51415
52666
|
try {
|
|
51416
52667
|
const entryFile = await resolveEntryFile();
|
|
51417
|
-
const content = await
|
|
52668
|
+
const content = await readFile28(entryFile, "utf-8");
|
|
51418
52669
|
const lines = content.split("\n");
|
|
51419
52670
|
const skills2 = [];
|
|
51420
52671
|
for (const line of lines) {
|
|
@@ -51582,9 +52833,13 @@ async function skillsUpdateCommand(name, options) {
|
|
|
51582
52833
|
|
|
51583
52834
|
// packages/cli/src/cli.ts
|
|
51584
52835
|
var __filename4 = fileURLToPath4(import.meta.url);
|
|
51585
|
-
var __dirname4 =
|
|
52836
|
+
var __dirname4 = dirname27(__filename4);
|
|
52837
|
+
var CLI_VERSION = getPackageVersion(__dirname3, "./package.json");
|
|
51586
52838
|
var program = new Command();
|
|
51587
|
-
program.name("prs").description("PromptScript CLI - Standardize AI instructions").version(
|
|
52839
|
+
program.name("prs").description("PromptScript CLI - Standardize AI instructions").version(CLI_VERSION).option("--verbose", "Enable verbose output").option("--debug", "Enable debug output (includes verbose)").option("--quiet", "Suppress non-error output").hook("preAction", async (thisCommand, actionCommand) => {
|
|
52840
|
+
if (actionCommand.name() === "__telemetry-flush" && process.env["PROMPTSCRIPT_TELEMETRY_FLUSH"] === "1") {
|
|
52841
|
+
return;
|
|
52842
|
+
}
|
|
51588
52843
|
const opts = thisCommand.opts();
|
|
51589
52844
|
if (opts["quiet"]) {
|
|
51590
52845
|
setContext({ logLevel: 0 /* Quiet */ });
|
|
@@ -51598,7 +52853,8 @@ program.name("prs").description("PromptScript CLI - Standardize AI instructions"
|
|
|
51598
52853
|
} else if (process.env["PROMPTSCRIPT_VERBOSE"] === "1" || process.env["PROMPTSCRIPT_VERBOSE"] === "true") {
|
|
51599
52854
|
setContext({ logLevel: 2 /* Verbose */ });
|
|
51600
52855
|
}
|
|
51601
|
-
|
|
52856
|
+
await prepareCliTelemetry(actionCommand, CLI_VERSION);
|
|
52857
|
+
if (actionCommand.name() === "update-check" || actionCommand.name() === "telemetry" || actionCommand.name() === "__telemetry-flush") {
|
|
51602
52858
|
return;
|
|
51603
52859
|
}
|
|
51604
52860
|
const currentVersion = getPackageVersion(__dirname3, "./package.json");
|
|
@@ -51607,6 +52863,8 @@ program.name("prs").description("PromptScript CLI - Standardize AI instructions"
|
|
|
51607
52863
|
printUpdateNotification(updateInfo);
|
|
51608
52864
|
}
|
|
51609
52865
|
});
|
|
52866
|
+
}).hook("postAction", () => {
|
|
52867
|
+
finishCliTelemetry();
|
|
51610
52868
|
});
|
|
51611
52869
|
program.command("init").description("Initialize PromptScript in current directory").option("-n, --name <name>", "Project name (auto-detected from package.json, etc.)").option("-t, --team <team>", "Team namespace").option("--inherit <path>", "Inheritance path (e.g., @company/team)").option("--registry <path>", "Registry path").option("--targets <targets...>", "Target AI tools (for example: github, claude, cursor)").option("-i, --interactive", "Force interactive mode").option("-y, --yes", "Skip prompts; requires explicit, detected, or configured targets").option("-f, --force", "Force reinitialize even if already initialized").option("-m, --migrate", "Deprecated: run migration during initialization").option("--auto-import", "Automatically import existing instruction files (static)").option("--backup", "Create .prs-backup/ before replacing files").option("--dry-run", "Preview files without writing").option("--no-hooks", "Skip auto-compile hook installation for AI tools").action((opts) => {
|
|
51612
52870
|
if (opts.migrate) {
|
|
@@ -51628,6 +52886,8 @@ program.command("pull").description("Pull updates from registry").option("-f, --
|
|
|
51628
52886
|
program.command("diff").description("Show diff for compiled output").option("-t, --target <target>", "Specific target").option("-a, --all", "Show diff for all targets at once").option("--full", "Show full diff without truncation").option("--no-pager", "Disable pager output").option("--color", "Force colored output").option("--no-color", "Disable colored output").action(diffCommand);
|
|
51629
52887
|
program.command("check").description("Check configuration and dependencies health").action(checkCommand);
|
|
51630
52888
|
program.command("update-check").description("Check for CLI updates").action(updateCheckCommand);
|
|
52889
|
+
program.command("telemetry").argument("<action>", "Action: status, enable, or disable").description("Manage anonymous usage telemetry").action(telemetryCommand);
|
|
52890
|
+
program.command("__telemetry-flush", { hidden: true }).description("Flush queued anonymous telemetry").action(flushCliTelemetry);
|
|
51631
52891
|
program.command("serve").description("Start local development server for playground").option("-p, --port <port>", "Port to listen on", "3000").option("--host <host>", "Host to bind to", "127.0.0.1").option("--read-only", "Disable file modifications").option("--cors-origin <origin>", "Allowed CORS origin", "https://getpromptscript.dev").action(async (opts) => {
|
|
51632
52892
|
const { serveCommand: serveCommand2 } = await Promise.resolve().then(() => (init_serve(), serve_exports));
|
|
51633
52893
|
await serveCommand2(opts);
|
|
@@ -51653,11 +52913,18 @@ skills.command("list").description("List skills imported in the current project"
|
|
|
51653
52913
|
skills.command("update [name]").description("Update lock entries for markdown-sourced skills").option("--dry-run", "Preview changes without writing").option("--skip-validation", "Skip SKILL.md frontmatter validation (not recommended)").option("--strict", "Treat validation warnings as errors").action((name, opts) => skillsUpdateCommand(name, opts));
|
|
51654
52914
|
var registry = program.command("registry").description("Manage PromptScript registries");
|
|
51655
52915
|
registerRegistryCommands(registry);
|
|
51656
|
-
function run(args = process.argv) {
|
|
51657
|
-
|
|
52916
|
+
async function run(args = process.argv) {
|
|
52917
|
+
try {
|
|
52918
|
+
await program.parseAsync(args);
|
|
52919
|
+
} catch (error) {
|
|
52920
|
+
finishCliTelemetry("error");
|
|
52921
|
+
throw error;
|
|
52922
|
+
} finally {
|
|
52923
|
+
finishCliTelemetry();
|
|
52924
|
+
}
|
|
51658
52925
|
}
|
|
51659
52926
|
if (process.argv[1] && import.meta.url === pathToFileURL(process.argv[1]).href) {
|
|
51660
|
-
run();
|
|
52927
|
+
await run();
|
|
51661
52928
|
}
|
|
51662
52929
|
|
|
51663
52930
|
// packages/cli/src/index.ts
|