@danieldeusing/seedr 0.1.134 → 0.1.136
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +4 -0
- package/dist/{chunk-7RMRI2TO.js → chunk-D4Y67NUX.js} +3 -1
- package/dist/cli.js +118 -26
- package/dist/index.js +1 -1
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -34,6 +34,10 @@ paths, no IP stored (the server keeps only the country it derives from the reque
|
|
|
34
34
|
Events are deleted after 90 days. Nothing is sent for failed installs or `--dry-run`, and a
|
|
35
35
|
failing endpoint never affects an installation.
|
|
36
36
|
|
|
37
|
+
Only installs from seedr's own registry are counted. With `SEEDR_REGISTRY_URL` or
|
|
38
|
+
`SEEDR_REGISTRY_DIR` pointing at another registry nothing is sent, unless
|
|
39
|
+
`SEEDR_ANALYTICS_URL` names that registry's own endpoint.
|
|
40
|
+
|
|
37
41
|
Opt out by setting the variable to any value:
|
|
38
42
|
|
|
39
43
|
```bash
|
|
@@ -4,7 +4,7 @@
|
|
|
4
4
|
import { readFile as readFile3, readdir, writeFile as writeFile2, mkdir as mkdir2, mkdtemp, rm as rm2 } from "fs/promises";
|
|
5
5
|
import { existsSync as existsSync11 } from "fs";
|
|
6
6
|
import { tmpdir } from "os";
|
|
7
|
-
import { join as join13, dirname as dirname6 } from "path";
|
|
7
|
+
import { basename as basename6, join as join13, dirname as dirname6 } from "path";
|
|
8
8
|
import { fileURLToPath } from "url";
|
|
9
9
|
|
|
10
10
|
// ../registry-ops/src/paths.ts
|
|
@@ -519,6 +519,7 @@ function resolveLocalRegistryPath() {
|
|
|
519
519
|
var REGISTRY_PATH = process.env.SEEDR_REGISTRY_DIR || resolveLocalRegistryPath();
|
|
520
520
|
var DEFAULT_REGISTRY_URL = "https://raw.githubusercontent.com/danieldeusing/seedr/main/registry";
|
|
521
521
|
var REGISTRY_URL = (process.env.SEEDR_REGISTRY_URL || DEFAULT_REGISTRY_URL).replace(/\/+$/, "");
|
|
522
|
+
var USES_DEFAULT_REGISTRY = !process.env.SEEDR_REGISTRY_DIR && REGISTRY_URL === DEFAULT_REGISTRY_URL && (REGISTRY_PATH === null || basename6(REGISTRY_PATH) === DEFAULT_REGISTRY_DIR);
|
|
522
523
|
var REGISTRY_ROOT_URL = REGISTRY_URL.replace(/\/registry$/, "");
|
|
523
524
|
function assertSafeNodeName(name) {
|
|
524
525
|
if (name === "" || name === "." || name.includes("/") || name.includes("\\") || name.includes("..")) {
|
|
@@ -1368,6 +1369,7 @@ export {
|
|
|
1368
1369
|
copyDirectory,
|
|
1369
1370
|
parseGitHubRepo,
|
|
1370
1371
|
getEffectiveSourceRevision,
|
|
1372
|
+
USES_DEFAULT_REGISTRY,
|
|
1371
1373
|
loadManifest,
|
|
1372
1374
|
getItem,
|
|
1373
1375
|
getItemsBySlug,
|
package/dist/cli.js
CHANGED
|
@@ -6,6 +6,7 @@ import {
|
|
|
6
6
|
CODING_AGENTS,
|
|
7
7
|
MAX_SLUG_LENGTH,
|
|
8
8
|
SLUG_PATTERN,
|
|
9
|
+
USES_DEFAULT_REGISTRY,
|
|
9
10
|
assertDirectoryWithin,
|
|
10
11
|
assertOverwritable,
|
|
11
12
|
assertSafePathSegment,
|
|
@@ -67,7 +68,7 @@ import {
|
|
|
67
68
|
writeFileAtomic,
|
|
68
69
|
writeJson,
|
|
69
70
|
writeTextFile
|
|
70
|
-
} from "./chunk-
|
|
71
|
+
} from "./chunk-D4Y67NUX.js";
|
|
71
72
|
|
|
72
73
|
// src/cli.ts
|
|
73
74
|
import { Command as Command5 } from "commander";
|
|
@@ -141,17 +142,21 @@ function validateType(value) {
|
|
|
141
142
|
// src/utils/analytics.ts
|
|
142
143
|
var ANALYTICS_URL = "https://seedr.danieldeusing.de/api/installs";
|
|
143
144
|
var TELEMETRY_OPT_OUT_VARIABLE = "SEEDR_NO_TELEMETRY";
|
|
145
|
+
var ANALYTICS_URL_VARIABLE = "SEEDR_ANALYTICS_URL";
|
|
144
146
|
var REQUEST_TIMEOUT_MS = 4e3;
|
|
145
|
-
var TELEMETRY_HELP_TEXT = `Sends one anonymous install event per successful agent target to ${ANALYTICS_URL}; set ${TELEMETRY_OPT_OUT_VARIABLE}=1 to disable`;
|
|
147
|
+
var TELEMETRY_HELP_TEXT = `Sends one anonymous install event per successful agent target to ${ANALYTICS_URL} (installs from another registry send nothing unless ${ANALYTICS_URL_VARIABLE} names an endpoint); set ${TELEMETRY_OPT_OUT_VARIABLE}=1 to disable`;
|
|
146
148
|
function isTelemetryDisabled(env = process.env) {
|
|
147
149
|
return env[TELEMETRY_OPT_OUT_VARIABLE] !== void 0;
|
|
148
150
|
}
|
|
151
|
+
function analyticsEndpoint(env = process.env) {
|
|
152
|
+
return env[ANALYTICS_URL_VARIABLE] || (USES_DEFAULT_REGISTRY ? ANALYTICS_URL : null);
|
|
153
|
+
}
|
|
149
154
|
function cliVersion() {
|
|
150
|
-
return true ? "0.1.
|
|
155
|
+
return true ? "0.1.136" : "dev";
|
|
151
156
|
}
|
|
152
|
-
async function sendEvent(event) {
|
|
157
|
+
async function sendEvent(endpoint, event) {
|
|
153
158
|
try {
|
|
154
|
-
await fetch(
|
|
159
|
+
await fetch(endpoint, {
|
|
155
160
|
method: "POST",
|
|
156
161
|
headers: { "Content-Type": "application/json" },
|
|
157
162
|
body: JSON.stringify(event),
|
|
@@ -161,9 +166,10 @@ async function sendEvent(event) {
|
|
|
161
166
|
}
|
|
162
167
|
}
|
|
163
168
|
function trackInstalls(slug, type, results, scope) {
|
|
164
|
-
|
|
169
|
+
const endpoint = analyticsEndpoint();
|
|
170
|
+
if (isTelemetryDisabled() || endpoint === null) return Promise.resolve();
|
|
165
171
|
const version = cliVersion();
|
|
166
|
-
const sends = results.filter((result) => result.success).map((result) => sendEvent({ slug, type, tool: result.agent, scope, version }));
|
|
172
|
+
const sends = results.filter((result) => result.success).map((result) => sendEvent(endpoint, { slug, type, tool: result.agent, scope, version }));
|
|
167
173
|
return Promise.all(sends).then(() => void 0);
|
|
168
174
|
}
|
|
169
175
|
|
|
@@ -907,32 +913,59 @@ function transportOf(config) {
|
|
|
907
913
|
function definedEntries(object) {
|
|
908
914
|
return Object.fromEntries(Object.entries(object).filter(([, value]) => value !== void 0));
|
|
909
915
|
}
|
|
916
|
+
var PLACEHOLDER = /\$\{([A-Za-z_][A-Za-z0-9_]*)\}/g;
|
|
917
|
+
var WHOLE_PLACEHOLDER = /^\$\{([A-Za-z_][A-Za-z0-9_]*)\}$/;
|
|
918
|
+
var BEARER_PLACEHOLDER = /^Bearer \$\{([A-Za-z_][A-Za-z0-9_]*)\}$/;
|
|
919
|
+
var toOpenCodeRef = (value) => value.replace(PLACEHOLDER, "{env:$1}");
|
|
920
|
+
function mapValues(record, map) {
|
|
921
|
+
return record && Object.fromEntries(Object.entries(record).map(([key, value]) => [key, map(value)]));
|
|
922
|
+
}
|
|
910
923
|
function toOpenCodeServer(config) {
|
|
911
924
|
if (transportOf(config) === "stdio") {
|
|
912
925
|
if (!config.command) throw new Error(NO_COMMAND_ERROR);
|
|
913
926
|
return definedEntries({
|
|
914
927
|
type: "local",
|
|
915
|
-
command: [config.command, ...config.args ?? []],
|
|
916
|
-
environment: config.env,
|
|
928
|
+
command: [config.command, ...config.args ?? []].map(toOpenCodeRef),
|
|
929
|
+
environment: mapValues(config.env, toOpenCodeRef),
|
|
917
930
|
enabled: true
|
|
918
931
|
});
|
|
919
932
|
}
|
|
920
933
|
if (!config.url) throw new Error(NO_URL_ERROR);
|
|
921
|
-
return definedEntries({ type: "remote", url: config.url, headers: config.headers, enabled: true });
|
|
922
|
-
}
|
|
923
|
-
function
|
|
934
|
+
return definedEntries({ type: "remote", url: toOpenCodeRef(config.url), headers: mapValues(config.headers, toOpenCodeRef), enabled: true });
|
|
935
|
+
}
|
|
936
|
+
function codexPassthrough(config) {
|
|
937
|
+
const env = {};
|
|
938
|
+
const envVars = [];
|
|
939
|
+
for (const [key, value] of Object.entries(config.env ?? {})) {
|
|
940
|
+
if (WHOLE_PLACEHOLDER.exec(value)?.[1] === key) envVars.push(key);
|
|
941
|
+
else env[key] = value;
|
|
942
|
+
}
|
|
943
|
+
const headers = {};
|
|
944
|
+
const envHeaders = {};
|
|
945
|
+
let bearer = config.bearer_token_env_var || void 0;
|
|
946
|
+
for (const [key, value] of Object.entries(config.headers ?? {})) {
|
|
947
|
+
const bearerVar = key.toLowerCase() === "authorization" ? BEARER_PLACEHOLDER.exec(value)?.[1] : void 0;
|
|
948
|
+
const wholeVar = WHOLE_PLACEHOLDER.exec(value)?.[1];
|
|
949
|
+
if (bearerVar && !bearer) bearer = bearerVar;
|
|
950
|
+
else if (wholeVar) envHeaders[key] = wholeVar;
|
|
951
|
+
else headers[key] = value;
|
|
952
|
+
}
|
|
953
|
+
return { env, envVars, headers, envHeaders, bearer };
|
|
954
|
+
}
|
|
955
|
+
function codexMainEntries(config, passthrough) {
|
|
924
956
|
if (transportOf(config) === "stdio") {
|
|
925
957
|
if (!config.command) throw new Error(NO_COMMAND_ERROR);
|
|
926
958
|
return definedEntries({
|
|
927
959
|
command: config.command,
|
|
928
960
|
args: config.args && config.args.length > 0 ? config.args : void 0,
|
|
929
|
-
cwd: config.cwd || void 0
|
|
961
|
+
cwd: config.cwd || void 0,
|
|
962
|
+
env_vars: passthrough.envVars.length > 0 ? passthrough.envVars : void 0
|
|
930
963
|
});
|
|
931
964
|
}
|
|
932
965
|
if (!config.url) throw new Error(NO_URL_ERROR);
|
|
933
966
|
return definedEntries({
|
|
934
967
|
url: config.url,
|
|
935
|
-
bearer_token_env_var:
|
|
968
|
+
bearer_token_env_var: passthrough.bearer
|
|
936
969
|
});
|
|
937
970
|
}
|
|
938
971
|
function hasEntries(record) {
|
|
@@ -940,12 +973,16 @@ function hasEntries(record) {
|
|
|
940
973
|
}
|
|
941
974
|
function toCodexTables(name, config) {
|
|
942
975
|
const base = [CODEX_TABLE, name];
|
|
943
|
-
const
|
|
944
|
-
|
|
945
|
-
|
|
976
|
+
const passthrough = codexPassthrough(config);
|
|
977
|
+
const tables = [{ keyPath: base, entries: codexMainEntries(config, passthrough) }];
|
|
978
|
+
if (hasEntries(passthrough.env)) {
|
|
979
|
+
tables.push({ keyPath: [...base, "env"], entries: passthrough.env });
|
|
980
|
+
}
|
|
981
|
+
if (hasEntries(passthrough.headers)) {
|
|
982
|
+
tables.push({ keyPath: [...base, "http_headers"], entries: passthrough.headers });
|
|
946
983
|
}
|
|
947
|
-
if (hasEntries(
|
|
948
|
-
tables.push({ keyPath: [...base, "
|
|
984
|
+
if (hasEntries(passthrough.envHeaders)) {
|
|
985
|
+
tables.push({ keyPath: [...base, "env_http_headers"], entries: passthrough.envHeaders });
|
|
949
986
|
}
|
|
950
987
|
return tables;
|
|
951
988
|
}
|
|
@@ -2113,19 +2150,22 @@ var claudeTarget = {
|
|
|
2113
2150
|
kind: "file",
|
|
2114
2151
|
dir: (scope, cwd) => join7(scope === "user" ? claudeUserRoot() : join7(cwd, ".claude"), "rules"),
|
|
2115
2152
|
fileName: markdownFile,
|
|
2116
|
-
keepsFrontmatter: true
|
|
2153
|
+
keepsFrontmatter: true,
|
|
2154
|
+
scopeKey: "paths"
|
|
2117
2155
|
};
|
|
2118
2156
|
var antigravityTarget = {
|
|
2119
2157
|
kind: "file",
|
|
2120
2158
|
dir: (scope, cwd) => join7(scope === "user" ? join7(home2, ".gemini", "config") : join7(cwd, ".agents"), "rules"),
|
|
2121
2159
|
fileName: markdownFile,
|
|
2122
|
-
keepsFrontmatter: true
|
|
2160
|
+
keepsFrontmatter: true,
|
|
2161
|
+
scopeKey: "glob"
|
|
2123
2162
|
};
|
|
2124
2163
|
var copilotTarget = {
|
|
2125
2164
|
kind: "file",
|
|
2126
2165
|
dir: (scope, cwd) => join7(scope === "user" ? copilotUserRoot() : join7(cwd, ".github"), "instructions"),
|
|
2127
2166
|
fileName: (slug) => `${slug}.instructions.md`,
|
|
2128
|
-
keepsFrontmatter: true
|
|
2167
|
+
keepsFrontmatter: true,
|
|
2168
|
+
scopeKey: "applyTo"
|
|
2129
2169
|
};
|
|
2130
2170
|
var codexTarget = {
|
|
2131
2171
|
kind: "section",
|
|
@@ -2159,9 +2199,59 @@ function stripFrontmatter(content) {
|
|
|
2159
2199
|
const match = /^---\r?\n[\s\S]*?\r?\n---\r?\n?/.exec(content);
|
|
2160
2200
|
return match ? content.slice(match[0].length) : content;
|
|
2161
2201
|
}
|
|
2202
|
+
var FRONTMATTER = /^---\r?\n([\s\S]*?)\r?\n---(\r?\n|$)/;
|
|
2203
|
+
var SCOPE_LINE = /^(applyTo|paths|glob):(.*)$/;
|
|
2204
|
+
var unquote = (value) => value.trim().replace(/^(["'])(.*)\1$/, "$2").trim();
|
|
2205
|
+
function continuationLength(lines, index) {
|
|
2206
|
+
let count = 0;
|
|
2207
|
+
while (index + count + 1 < lines.length && /^(\s+\S|-\s)/.test(lines[index + count + 1])) count++;
|
|
2208
|
+
return count;
|
|
2209
|
+
}
|
|
2210
|
+
function splitGlobs(value) {
|
|
2211
|
+
const parts = [""];
|
|
2212
|
+
let depth = 0;
|
|
2213
|
+
for (const char of value) {
|
|
2214
|
+
if (char === "{") depth++;
|
|
2215
|
+
else if (char === "}") depth = Math.max(0, depth - 1);
|
|
2216
|
+
if (char === "," && depth === 0) parts.push("");
|
|
2217
|
+
else parts[parts.length - 1] += char;
|
|
2218
|
+
}
|
|
2219
|
+
return parts;
|
|
2220
|
+
}
|
|
2221
|
+
function ruleScope(content) {
|
|
2222
|
+
const lines = FRONTMATTER.exec(content)?.[1]?.split(/\r?\n/) ?? [];
|
|
2223
|
+
for (let i = 0; i < lines.length; i++) {
|
|
2224
|
+
const inline = SCOPE_LINE.exec(lines[i])?.[2]?.trim();
|
|
2225
|
+
if (inline === void 0) continue;
|
|
2226
|
+
const values = inline ? splitGlobs(inline.startsWith("[") ? inline.slice(1, -1) : unquote(inline)) : lines.slice(i + 1, i + 1 + continuationLength(lines, i)).map((line) => line.replace(/^\s*-\s*/, ""));
|
|
2227
|
+
return values.map(unquote).filter((glob) => glob.length > 0);
|
|
2228
|
+
}
|
|
2229
|
+
return [];
|
|
2230
|
+
}
|
|
2231
|
+
function withScopeKey(content, key) {
|
|
2232
|
+
const globs = ruleScope(content);
|
|
2233
|
+
const match = FRONTMATTER.exec(content);
|
|
2234
|
+
if (globs.length === 0 || !match) return content;
|
|
2235
|
+
const lines = match[1].split(/\r?\n/);
|
|
2236
|
+
if (lines.some((line) => SCOPE_LINE.exec(line)?.[1] === key)) return content;
|
|
2237
|
+
const kept = [];
|
|
2238
|
+
for (let i = 0; i < lines.length; i++) {
|
|
2239
|
+
if (SCOPE_LINE.test(lines[i])) i += continuationLength(lines, i);
|
|
2240
|
+
else kept.push(lines[i]);
|
|
2241
|
+
}
|
|
2242
|
+
const scope = key === "paths" ? ["paths:", ...globs.map((glob) => ` - ${JSON.stringify(glob)}`)] : [`${key}: ${JSON.stringify(globs.join(", "))}`];
|
|
2243
|
+
return `---
|
|
2244
|
+
${[...kept, ...scope].join("\n")}
|
|
2245
|
+
---${match[2]}${content.slice(match[0].length)}`;
|
|
2246
|
+
}
|
|
2247
|
+
var scopeProse = (globs) => `Applies to files matching ${globs.map((glob) => `\`${glob}\``).join(", ")}.`;
|
|
2162
2248
|
function upsertSection(document, slug, content) {
|
|
2249
|
+
const globs = ruleScope(content);
|
|
2250
|
+
const body = stripFrontmatter(content).trim();
|
|
2163
2251
|
const block = `${sectionStart(slug)}
|
|
2164
|
-
${
|
|
2252
|
+
${globs.length > 0 ? `${scopeProse(globs)}
|
|
2253
|
+
|
|
2254
|
+
` : ""}${body}
|
|
2165
2255
|
${sectionEnd(slug)}`;
|
|
2166
2256
|
const existing = sectionPattern(slug);
|
|
2167
2257
|
if (existing.test(document)) {
|
|
@@ -2220,7 +2310,7 @@ async function installRuleForAgent(item, agent, scope, force, cwd, content) {
|
|
|
2220
2310
|
await assertOverwritable(destination, force);
|
|
2221
2311
|
await writeFile2(
|
|
2222
2312
|
destination,
|
|
2223
|
-
target.keepsFrontmatter ? content : stripFrontmatter(content),
|
|
2313
|
+
target.keepsFrontmatter ? withScopeKey(content, target.scopeKey) : stripFrontmatter(content),
|
|
2224
2314
|
"utf-8"
|
|
2225
2315
|
);
|
|
2226
2316
|
} else {
|
|
@@ -2279,6 +2369,8 @@ async function getInstalledRules(agent, scope, cwd = process.cwd()) {
|
|
|
2279
2369
|
async function planRule(item, agents, scope, _method, cwd) {
|
|
2280
2370
|
assertValidSlug(item.slug, SLUG_LABEL4);
|
|
2281
2371
|
const changes = [];
|
|
2372
|
+
const merges = agents.some((agent) => ruleTargetFor(agent).kind === "section");
|
|
2373
|
+
const globs = merges ? ruleScope(await readRuleContent(item)) : [];
|
|
2282
2374
|
for (const agent of agents) {
|
|
2283
2375
|
const target = ruleTargetFor(agent);
|
|
2284
2376
|
const destination = await destinationFor(target, item.slug, scope, cwd);
|
|
@@ -2287,7 +2379,7 @@ async function planRule(item, agents, scope, _method, cwd) {
|
|
|
2287
2379
|
agent,
|
|
2288
2380
|
kind: present ? "modify" : "create",
|
|
2289
2381
|
path: destination,
|
|
2290
|
-
detail: target.kind === "file" ? "rule file" : `merged section <!-- seedr:rule:${item.slug} --> (the rest of the file is untouched)`
|
|
2382
|
+
detail: target.kind === "file" ? "rule file" : `merged section <!-- seedr:rule:${item.slug} --> (the rest of the file is untouched)` + (globs.length > 0 ? `; ${CODING_AGENTS[agent].name} has no path scope, so ${globs.map((glob) => `\`${glob}\``).join(", ")} is advisory prose` : "")
|
|
2291
2383
|
});
|
|
2292
2384
|
}
|
|
2293
2385
|
return changes;
|
|
@@ -2993,7 +3085,7 @@ var initCommand = new Command4("init").description("Initialize coding agent conf
|
|
|
2993
3085
|
// package.json
|
|
2994
3086
|
var package_default = {
|
|
2995
3087
|
name: "@danieldeusing/seedr",
|
|
2996
|
-
version: "0.1.
|
|
3088
|
+
version: "0.1.136",
|
|
2997
3089
|
description: "Seed your projects with AI configurations",
|
|
2998
3090
|
type: "module",
|
|
2999
3091
|
bin: {
|
package/dist/index.js
CHANGED