switchroom 0.13.48 → 0.13.50
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/cli/switchroom.js
CHANGED
|
@@ -47249,6 +47249,20 @@ function dispatchTool(name, args) {
|
|
|
47249
47249
|
parseMode = "json";
|
|
47250
47250
|
break;
|
|
47251
47251
|
}
|
|
47252
|
+
case "skill_clone_to_personal": {
|
|
47253
|
+
const a = args;
|
|
47254
|
+
if (!a.source || typeof a.source !== "string") {
|
|
47255
|
+
return errorText("skill_clone_to_personal: source is required");
|
|
47256
|
+
}
|
|
47257
|
+
const base = ["skill", "clone-to-personal", a.source];
|
|
47258
|
+
if (a.agent)
|
|
47259
|
+
base.push("--agent", a.agent);
|
|
47260
|
+
if (a.name)
|
|
47261
|
+
base.push("--name", a.name);
|
|
47262
|
+
cliArgs = base;
|
|
47263
|
+
parseMode = "json";
|
|
47264
|
+
break;
|
|
47265
|
+
}
|
|
47252
47266
|
default:
|
|
47253
47267
|
return errorText(`unknown tool: ${name}`);
|
|
47254
47268
|
}
|
|
@@ -47488,6 +47502,26 @@ var init_server3 = __esm(() => {
|
|
|
47488
47502
|
type: "object",
|
|
47489
47503
|
properties: {}
|
|
47490
47504
|
}
|
|
47505
|
+
},
|
|
47506
|
+
{
|
|
47507
|
+
name: "skill_clone_to_personal",
|
|
47508
|
+
description: "Fork a shared or bundled skill into this agent's writable workspace. " + "Use when you find a defect or gap in a skill you depend on and want " + "to fix it yourself \u2014 the upstream source is untouched, your fork is " + "yours to edit via `skill_edit_personal`. Source format: " + "`shared:<name>` or `bundled:<name>`. Same validation gates as " + "init/edit. Pre-existing personal-<name> is refused (use edit or " + "remove first). No operator approval.",
|
|
47509
|
+
inputSchema: {
|
|
47510
|
+
type: "object",
|
|
47511
|
+
required: ["source"],
|
|
47512
|
+
properties: {
|
|
47513
|
+
source: {
|
|
47514
|
+
type: "string",
|
|
47515
|
+
pattern: "^(shared|bundled):[a-z0-9][a-z0-9_-]{0,62}$",
|
|
47516
|
+
description: "Source skill: `shared:<name>` (operator pool) or `bundled:<name>` (shipped)."
|
|
47517
|
+
},
|
|
47518
|
+
name: {
|
|
47519
|
+
type: "string",
|
|
47520
|
+
pattern: "^[a-z0-9][a-z0-9_-]{0,62}$",
|
|
47521
|
+
description: "Optional destination slug (defaults to the source slug)."
|
|
47522
|
+
}
|
|
47523
|
+
}
|
|
47524
|
+
}
|
|
47491
47525
|
}
|
|
47492
47526
|
];
|
|
47493
47527
|
});
|
|
@@ -47913,8 +47947,8 @@ var {
|
|
|
47913
47947
|
} = import__.default;
|
|
47914
47948
|
|
|
47915
47949
|
// src/build-info.ts
|
|
47916
|
-
var VERSION = "0.13.
|
|
47917
|
-
var COMMIT_SHA = "
|
|
47950
|
+
var VERSION = "0.13.50";
|
|
47951
|
+
var COMMIT_SHA = "99e127b1";
|
|
47918
47952
|
|
|
47919
47953
|
// src/cli/agent.ts
|
|
47920
47954
|
init_source();
|
|
@@ -49955,6 +49989,16 @@ function buildSettingsHooksBlock(p) {
|
|
|
49955
49989
|
timeout: 5
|
|
49956
49990
|
}
|
|
49957
49991
|
]
|
|
49992
|
+
},
|
|
49993
|
+
{
|
|
49994
|
+
matcher: "^(Read|Edit|Write|MultiEdit|NotebookEdit|Bash)$",
|
|
49995
|
+
hooks: [
|
|
49996
|
+
{
|
|
49997
|
+
type: "command",
|
|
49998
|
+
command: wrap("hook:repo-context-pretool", `node "${join8(DOCKER_HOOKS_PATH, "repo-context-pretool.mjs")}"`),
|
|
49999
|
+
timeout: 5
|
|
50000
|
+
}
|
|
50001
|
+
]
|
|
49958
50002
|
}
|
|
49959
50003
|
] : [];
|
|
49960
50004
|
const switchroomPostToolUse = useSwitchroomPlugin ? [
|
|
@@ -77596,6 +77640,87 @@ init_source();
|
|
|
77596
77640
|
var PERSONAL_PREFIX = "personal-";
|
|
77597
77641
|
var TRASH_DIRNAME = "skills-trash";
|
|
77598
77642
|
var TRASH_TTL_MS = 24 * 60 * 60 * 1000;
|
|
77643
|
+
var PERSONAL_SKILLS_SUBPATH = "personal-skills";
|
|
77644
|
+
function resolveConfigSkillsDir(agent) {
|
|
77645
|
+
const override = process.env.SWITCHROOM_CONFIG_DIR;
|
|
77646
|
+
const candidate = override ? resolve45(override) : join69(homedir39(), ".switchroom-config");
|
|
77647
|
+
if (!existsSync76(candidate))
|
|
77648
|
+
return null;
|
|
77649
|
+
return join69(candidate, "agents", agent, PERSONAL_SKILLS_SUBPATH);
|
|
77650
|
+
}
|
|
77651
|
+
var MIRROR_PRIOR_TTL_MS = 24 * 60 * 60 * 1000;
|
|
77652
|
+
function sweepMirrorPriors(configSkillsRoot) {
|
|
77653
|
+
try {
|
|
77654
|
+
if (!existsSync76(configSkillsRoot))
|
|
77655
|
+
return;
|
|
77656
|
+
const now = Date.now();
|
|
77657
|
+
for (const ent of readdirSync30(configSkillsRoot)) {
|
|
77658
|
+
const m = /^\.(?:.+)-(?:prior|trash)-(\d+)$/.exec(ent);
|
|
77659
|
+
if (!m)
|
|
77660
|
+
continue;
|
|
77661
|
+
const ts = Number(m[1]);
|
|
77662
|
+
if (!Number.isFinite(ts))
|
|
77663
|
+
continue;
|
|
77664
|
+
if (now - ts < MIRROR_PRIOR_TTL_MS)
|
|
77665
|
+
continue;
|
|
77666
|
+
try {
|
|
77667
|
+
rmSync17(join69(configSkillsRoot, ent), { recursive: true, force: true });
|
|
77668
|
+
} catch {}
|
|
77669
|
+
}
|
|
77670
|
+
} catch {}
|
|
77671
|
+
}
|
|
77672
|
+
function mirrorToConfigRepo(agent, name, liveSkillDir) {
|
|
77673
|
+
const configSkillsRoot = resolveConfigSkillsDir(agent);
|
|
77674
|
+
if (!configSkillsRoot)
|
|
77675
|
+
return;
|
|
77676
|
+
const dest = join69(configSkillsRoot, name);
|
|
77677
|
+
try {
|
|
77678
|
+
if (liveSkillDir !== null) {
|
|
77679
|
+
try {
|
|
77680
|
+
const st = lstatSync9(liveSkillDir);
|
|
77681
|
+
if (st.isSymbolicLink()) {
|
|
77682
|
+
process.stderr.write(source_default.yellow(`warning: refusing to mirror ${liveSkillDir} \u2014 source is a symlink
|
|
77683
|
+
`));
|
|
77684
|
+
return;
|
|
77685
|
+
}
|
|
77686
|
+
} catch {}
|
|
77687
|
+
}
|
|
77688
|
+
if (liveSkillDir === null) {
|
|
77689
|
+
sweepMirrorPriors(configSkillsRoot);
|
|
77690
|
+
if (existsSync76(dest)) {
|
|
77691
|
+
const trash = join69(configSkillsRoot, `.${name}-trash-${Date.now()}`);
|
|
77692
|
+
renameSync17(dest, trash);
|
|
77693
|
+
}
|
|
77694
|
+
return;
|
|
77695
|
+
}
|
|
77696
|
+
mkdirSync41(configSkillsRoot, { recursive: true, mode: 493 });
|
|
77697
|
+
sweepMirrorPriors(configSkillsRoot);
|
|
77698
|
+
const staging = mkdtempSync6(join69(configSkillsRoot, `.${name}-staging-`));
|
|
77699
|
+
const walk2 = (src, dst) => {
|
|
77700
|
+
mkdirSync41(dst, { recursive: true, mode: 493 });
|
|
77701
|
+
for (const ent of readdirSync30(src, { withFileTypes: true })) {
|
|
77702
|
+
const s = join69(src, ent.name);
|
|
77703
|
+
const d = join69(dst, ent.name);
|
|
77704
|
+
if (ent.isSymbolicLink())
|
|
77705
|
+
continue;
|
|
77706
|
+
if (ent.isDirectory())
|
|
77707
|
+
walk2(s, d);
|
|
77708
|
+
else if (ent.isFile()) {
|
|
77709
|
+
writeFileSync35(d, readFileSync61(s));
|
|
77710
|
+
}
|
|
77711
|
+
}
|
|
77712
|
+
};
|
|
77713
|
+
walk2(liveSkillDir, staging);
|
|
77714
|
+
if (existsSync76(dest)) {
|
|
77715
|
+
const prior = join69(configSkillsRoot, `.${name}-prior-${Date.now()}`);
|
|
77716
|
+
renameSync17(dest, prior);
|
|
77717
|
+
}
|
|
77718
|
+
renameSync17(staging, dest);
|
|
77719
|
+
} catch (err2) {
|
|
77720
|
+
process.stderr.write(source_default.yellow(`warning: mirror to ${dest} failed (${err2.message ?? err2}); ` + `live copy still works, but this skill is not version-controlled until next successful sync.
|
|
77721
|
+
`));
|
|
77722
|
+
}
|
|
77723
|
+
}
|
|
77599
77724
|
function fail3(msg, exit = 2) {
|
|
77600
77725
|
console.error(source_default.red(`error: ${msg}`));
|
|
77601
77726
|
process.exit(exit);
|
|
@@ -77832,14 +77957,6 @@ function loadValidateWrite(agentsRoot, agent, name, files, ensureNew) {
|
|
|
77832
77957
|
process.exit(3);
|
|
77833
77958
|
}
|
|
77834
77959
|
writePersonalSkill(target, files);
|
|
77835
|
-
console.log(JSON.stringify({
|
|
77836
|
-
ok: true,
|
|
77837
|
-
action: ensureNew ? "init" : "edit",
|
|
77838
|
-
agent,
|
|
77839
|
-
name,
|
|
77840
|
-
path: target,
|
|
77841
|
-
files: Object.keys(files).length
|
|
77842
|
-
}));
|
|
77843
77960
|
}
|
|
77844
77961
|
function loadFiles(opts) {
|
|
77845
77962
|
if (opts.from === undefined) {
|
|
@@ -77863,6 +77980,16 @@ function initPersonalAction(name, opts) {
|
|
|
77863
77980
|
const agentsRoot = resolveAgentsRoot(opts);
|
|
77864
77981
|
const files = loadFiles(opts);
|
|
77865
77982
|
loadValidateWrite(agentsRoot, agent, name, files, true);
|
|
77983
|
+
const skillDir = personalSkillDir(agentsRoot, agent, name);
|
|
77984
|
+
mirrorToConfigRepo(agent, name, skillDir);
|
|
77985
|
+
console.log(JSON.stringify({
|
|
77986
|
+
ok: true,
|
|
77987
|
+
action: "init",
|
|
77988
|
+
agent,
|
|
77989
|
+
name,
|
|
77990
|
+
path: skillDir,
|
|
77991
|
+
files: Object.keys(files).length
|
|
77992
|
+
}));
|
|
77866
77993
|
appendAudit(agent, "skill.init_personal", { name, files: Object.keys(files).length }, 0);
|
|
77867
77994
|
}
|
|
77868
77995
|
function editPersonalAction(name, opts) {
|
|
@@ -77870,8 +77997,126 @@ function editPersonalAction(name, opts) {
|
|
|
77870
77997
|
const agentsRoot = resolveAgentsRoot(opts);
|
|
77871
77998
|
const files = loadFiles(opts);
|
|
77872
77999
|
loadValidateWrite(agentsRoot, agent, name, files, false);
|
|
78000
|
+
const skillDir = personalSkillDir(agentsRoot, agent, name);
|
|
78001
|
+
mirrorToConfigRepo(agent, name, skillDir);
|
|
78002
|
+
console.log(JSON.stringify({
|
|
78003
|
+
ok: true,
|
|
78004
|
+
action: "edit",
|
|
78005
|
+
agent,
|
|
78006
|
+
name,
|
|
78007
|
+
path: skillDir,
|
|
78008
|
+
files: Object.keys(files).length
|
|
78009
|
+
}));
|
|
77873
78010
|
appendAudit(agent, "skill.edit_personal", { name, files: Object.keys(files).length }, 0);
|
|
77874
78011
|
}
|
|
78012
|
+
var CLONE_SOURCE_RE = /^(shared|bundled):([a-z0-9][a-z0-9_-]{0,62})$/;
|
|
78013
|
+
function defaultSharedRoot() {
|
|
78014
|
+
return join69(homedir39(), ".switchroom", "skills");
|
|
78015
|
+
}
|
|
78016
|
+
function defaultBundledRoot() {
|
|
78017
|
+
return join69(homedir39(), ".switchroom", "skills", "_bundled");
|
|
78018
|
+
}
|
|
78019
|
+
function resolveCloneSource(source, opts) {
|
|
78020
|
+
const m = CLONE_SOURCE_RE.exec(source);
|
|
78021
|
+
if (!m) {
|
|
78022
|
+
fail3(`source must be \`shared:<name>\` or \`bundled:<name>\` (got ${JSON.stringify(source)})`);
|
|
78023
|
+
}
|
|
78024
|
+
const tier = m[1];
|
|
78025
|
+
const slug = m[2];
|
|
78026
|
+
const root = tier === "bundled" ? opts.bundledRoot ?? defaultBundledRoot() : opts.sharedRoot ?? defaultSharedRoot();
|
|
78027
|
+
const dir = join69(root, slug);
|
|
78028
|
+
if (!existsSync76(dir)) {
|
|
78029
|
+
fail3(`clone source ${JSON.stringify(source)} not found at ${dir}; ` + `check \`switchroom skill search --tier ${tier}\``, 1);
|
|
78030
|
+
}
|
|
78031
|
+
const st = lstatSync9(dir);
|
|
78032
|
+
if (st.isSymbolicLink()) {
|
|
78033
|
+
fail3(`clone source ${JSON.stringify(source)} is a symlink at ${dir}; ` + `point clone at the canonical pool path instead`);
|
|
78034
|
+
}
|
|
78035
|
+
return { tier, slug, dir };
|
|
78036
|
+
}
|
|
78037
|
+
var CLONE_MAX_FILE_BYTES = 1024 * 1024;
|
|
78038
|
+
function readSourceFiles(dir) {
|
|
78039
|
+
const files = {};
|
|
78040
|
+
const walk2 = (sub) => {
|
|
78041
|
+
for (const ent of readdirSync30(sub, { withFileTypes: true })) {
|
|
78042
|
+
const full = join69(sub, ent.name);
|
|
78043
|
+
if (ent.isSymbolicLink()) {
|
|
78044
|
+
continue;
|
|
78045
|
+
}
|
|
78046
|
+
if (ent.isDirectory()) {
|
|
78047
|
+
walk2(full);
|
|
78048
|
+
continue;
|
|
78049
|
+
}
|
|
78050
|
+
if (ent.isFile()) {
|
|
78051
|
+
const rel = relative3(dir, full).replace(/\\/g, "/");
|
|
78052
|
+
try {
|
|
78053
|
+
const st = lstatSync9(full);
|
|
78054
|
+
if (st.size > CLONE_MAX_FILE_BYTES) {
|
|
78055
|
+
fail3(`clone source has oversized file ${rel} (${st.size} bytes > ${CLONE_MAX_FILE_BYTES}); ` + `refuse to read`, 3);
|
|
78056
|
+
}
|
|
78057
|
+
} catch {}
|
|
78058
|
+
files[rel] = readFileSync61(full, "utf-8");
|
|
78059
|
+
}
|
|
78060
|
+
}
|
|
78061
|
+
};
|
|
78062
|
+
walk2(dir);
|
|
78063
|
+
return files;
|
|
78064
|
+
}
|
|
78065
|
+
function rewriteSkillMdName(content, newName) {
|
|
78066
|
+
if (!content.startsWith(`---
|
|
78067
|
+
`) && !content.startsWith(`---\r
|
|
78068
|
+
`)) {
|
|
78069
|
+
return content;
|
|
78070
|
+
}
|
|
78071
|
+
const rest = content.slice(content.indexOf(`
|
|
78072
|
+
`) + 1);
|
|
78073
|
+
const endIdx = rest.indexOf(`
|
|
78074
|
+
---`);
|
|
78075
|
+
if (endIdx < 0)
|
|
78076
|
+
return content;
|
|
78077
|
+
const fm = rest.slice(0, endIdx);
|
|
78078
|
+
const body = rest.slice(endIdx);
|
|
78079
|
+
const patched = fm.replace(/^(\s*name\s*:)[ \t]*\S.*$/m, `$1 ${newName}`);
|
|
78080
|
+
return `---
|
|
78081
|
+
` + patched + body;
|
|
78082
|
+
}
|
|
78083
|
+
function clonePersonalAction(source, opts) {
|
|
78084
|
+
const agent = resolveAgent(opts);
|
|
78085
|
+
const agentsRoot = resolveAgentsRoot(opts);
|
|
78086
|
+
const src = resolveCloneSource(source, opts);
|
|
78087
|
+
const newName = opts.name ?? src.slug;
|
|
78088
|
+
if (!SKILL_SLUG_RE.test(newName)) {
|
|
78089
|
+
fail3(`destination name must match ${SKILL_SLUG_RE.source}: got ${JSON.stringify(newName)}`);
|
|
78090
|
+
}
|
|
78091
|
+
const files = readSourceFiles(src.dir);
|
|
78092
|
+
if (!files["SKILL.md"]) {
|
|
78093
|
+
fail3(`source ${JSON.stringify(source)} has no SKILL.md at ${src.dir}`);
|
|
78094
|
+
}
|
|
78095
|
+
if (newName !== src.slug) {
|
|
78096
|
+
files["SKILL.md"] = rewriteSkillMdName(files["SKILL.md"], newName);
|
|
78097
|
+
}
|
|
78098
|
+
loadValidateWrite(agentsRoot, agent, newName, files, true);
|
|
78099
|
+
const skillDir = personalSkillDir(agentsRoot, agent, newName);
|
|
78100
|
+
mirrorToConfigRepo(agent, newName, skillDir);
|
|
78101
|
+
console.log(JSON.stringify({
|
|
78102
|
+
ok: true,
|
|
78103
|
+
action: "clone_to_personal",
|
|
78104
|
+
agent,
|
|
78105
|
+
source,
|
|
78106
|
+
source_tier: src.tier,
|
|
78107
|
+
source_slug: src.slug,
|
|
78108
|
+
name: newName,
|
|
78109
|
+
path: skillDir,
|
|
78110
|
+
files: Object.keys(files).length
|
|
78111
|
+
}));
|
|
78112
|
+
appendAudit(agent, "skill.clone_to_personal", {
|
|
78113
|
+
source,
|
|
78114
|
+
source_tier: src.tier,
|
|
78115
|
+
source_slug: src.slug,
|
|
78116
|
+
name: newName,
|
|
78117
|
+
files: Object.keys(files).length
|
|
78118
|
+
}, 0);
|
|
78119
|
+
}
|
|
77875
78120
|
function removePersonalAction(name, opts) {
|
|
77876
78121
|
const agent = resolveAgent(opts);
|
|
77877
78122
|
const agentsRoot = resolveAgentsRoot(opts);
|
|
@@ -77899,6 +78144,7 @@ function removePersonalAction(name, opts) {
|
|
|
77899
78144
|
renameSync17(target, trashTarget);
|
|
77900
78145
|
const now = new Date(ts);
|
|
77901
78146
|
utimesSync(trashTarget, now, now);
|
|
78147
|
+
mirrorToConfigRepo(agent, name, null);
|
|
77902
78148
|
console.log(JSON.stringify({
|
|
77903
78149
|
ok: true,
|
|
77904
78150
|
action: "remove",
|
|
@@ -77964,6 +78210,9 @@ function registerSkillPersonalCommands(program3) {
|
|
|
77964
78210
|
parent.command("list-personal").description("List personal skills owned by this agent. JSON output by default.").option("--agent <name>", "Agent name (defaults to $SWITCHROOM_AGENT_NAME)").option("--root <path>", "Test-only override for agents-root dir").action(withConfigError(async (opts) => {
|
|
77965
78211
|
listPersonalAction(opts);
|
|
77966
78212
|
}));
|
|
78213
|
+
parent.command("clone-to-personal <source>").description("Fork a shared or bundled skill into this agent's writable workspace. " + "Source format: `shared:<name>` or `bundled:<name>`. The personal copy " + "becomes mutable via edit-personal; the upstream source is untouched. " + "Use --name to give the fork a different slug. No operator approval \u2014 " + "agent's own workspace.").option("--agent <name>", "Agent name (defaults to $SWITCHROOM_AGENT_NAME)").option("--name <slug>", "Override destination slug (default: source slug)").addOption(new Option("--root <path>").hideHelp()).addOption(new Option("--shared-root <path>").hideHelp()).addOption(new Option("--bundled-root <path>").hideHelp()).action(withConfigError(async (source, opts) => {
|
|
78214
|
+
clonePersonalAction(source, opts);
|
|
78215
|
+
}));
|
|
77967
78216
|
}
|
|
77968
78217
|
|
|
77969
78218
|
// src/cli/skill-search.ts
|
|
@@ -77978,10 +78227,10 @@ var AGENT_NAME_RE3 = /^[a-z][a-z0-9_-]{0,62}$/;
|
|
|
77978
78227
|
function defaultAgentsRoot() {
|
|
77979
78228
|
return resolve46(homedir40(), ".switchroom/agents");
|
|
77980
78229
|
}
|
|
77981
|
-
function
|
|
78230
|
+
function defaultSharedRoot2() {
|
|
77982
78231
|
return resolve46(homedir40(), ".switchroom/skills");
|
|
77983
78232
|
}
|
|
77984
|
-
function
|
|
78233
|
+
function defaultBundledRoot2() {
|
|
77985
78234
|
return resolve46(homedir40(), ".switchroom/skills/_bundled");
|
|
77986
78235
|
}
|
|
77987
78236
|
function readSkillFrontmatter(skillDir) {
|
|
@@ -78067,7 +78316,7 @@ function listPersonalSkills(agent, agentsRoot = defaultAgentsRoot()) {
|
|
|
78067
78316
|
}
|
|
78068
78317
|
return out;
|
|
78069
78318
|
}
|
|
78070
|
-
function listSharedSkills(sharedRoot =
|
|
78319
|
+
function listSharedSkills(sharedRoot = defaultSharedRoot2()) {
|
|
78071
78320
|
if (!existsSync77(sharedRoot))
|
|
78072
78321
|
return [];
|
|
78073
78322
|
const out = [];
|
|
@@ -78105,7 +78354,7 @@ function listSharedSkills(sharedRoot = defaultSharedRoot()) {
|
|
|
78105
78354
|
}
|
|
78106
78355
|
return out;
|
|
78107
78356
|
}
|
|
78108
|
-
function listBundledSkills(bundledRoot =
|
|
78357
|
+
function listBundledSkills(bundledRoot = defaultBundledRoot2()) {
|
|
78109
78358
|
if (!existsSync77(bundledRoot))
|
|
78110
78359
|
return [];
|
|
78111
78360
|
const out = [];
|
package/package.json
CHANGED
|
@@ -48732,10 +48732,10 @@ function sweepStaleTurnActiveMarker(stateDir, opts) {
|
|
|
48732
48732
|
}
|
|
48733
48733
|
|
|
48734
48734
|
// ../src/build-info.ts
|
|
48735
|
-
var VERSION = "0.13.
|
|
48736
|
-
var COMMIT_SHA = "
|
|
48737
|
-
var COMMIT_DATE = "2026-05-
|
|
48738
|
-
var LATEST_PR =
|
|
48735
|
+
var VERSION = "0.13.50";
|
|
48736
|
+
var COMMIT_SHA = "99e127b1";
|
|
48737
|
+
var COMMIT_DATE = "2026-05-25T22:55:41Z";
|
|
48738
|
+
var LATEST_PR = 1845;
|
|
48739
48739
|
var COMMITS_AHEAD_OF_TAG = 0;
|
|
48740
48740
|
|
|
48741
48741
|
// gateway/boot-version.ts
|