@primitive.ai/prim 0.1.0-alpha.41 → 0.1.0-alpha.43
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 +64 -6
- package/dist/{chunk-UWAJCF7P.js → chunk-BSZGI5RL.js} +1 -1
- package/dist/{chunk-S2O4P4A3.js → chunk-F5QCFBJ6.js} +14 -3
- package/dist/{chunk-HSZPTVKU.js → chunk-F7O7V6ZM.js} +88 -12
- package/dist/chunk-IMAIBPUC.js +210 -0
- package/dist/{chunk-H4OR42TJ.js → chunk-OKUXEBPT.js} +149 -36
- package/dist/{chunk-AAGJFO7C.js → chunk-QY75BQMF.js} +25 -1
- package/dist/chunk-S5NUHFAD.js +55 -0
- package/dist/chunk-UISU3A7W.js +202 -0
- package/dist/{chunk-26VA3ADF.js → chunk-W6PAKEDO.js} +32 -6
- package/dist/daemon/server.js +1150 -77
- package/dist/hooks/post-commit.js +5 -5
- package/dist/hooks/post-tool-use.js +31 -19
- package/dist/hooks/pre-commit.js +2 -2
- package/dist/hooks/pre-tool-use.js +3 -5
- package/dist/hooks/prim-hook.js +77 -20
- package/dist/hooks/session-start.js +79 -24
- package/dist/index.js +1609 -320
- package/dist/statusline-main.js +280 -0
- package/package.json +6 -5
- package/dist/chunk-LUPD2JSH.js +0 -78
|
@@ -0,0 +1,280 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
|
|
3
|
+
// src/commands/statusline.ts
|
|
4
|
+
import { readFileSync as readFileSync3 } from "fs";
|
|
5
|
+
import { dirname as dirname2, resolve as resolve2 } from "path";
|
|
6
|
+
import { fileURLToPath as fileURLToPath2 } from "url";
|
|
7
|
+
|
|
8
|
+
// src/client.ts
|
|
9
|
+
import { existsSync, readFileSync, writeFileSync } from "fs";
|
|
10
|
+
import { homedir } from "os";
|
|
11
|
+
import { join, resolve } from "path";
|
|
12
|
+
function loadEnvFile() {
|
|
13
|
+
const envVars = {};
|
|
14
|
+
const candidates = [".env.local", ".env"];
|
|
15
|
+
for (const file of candidates) {
|
|
16
|
+
const filePath = resolve(process.cwd(), file);
|
|
17
|
+
if (existsSync(filePath)) {
|
|
18
|
+
const content = readFileSync(filePath, "utf-8");
|
|
19
|
+
for (const line of content.split("\n")) {
|
|
20
|
+
const trimmed = line.trim();
|
|
21
|
+
if (!trimmed || trimmed.startsWith("#")) continue;
|
|
22
|
+
const eqIdx = trimmed.indexOf("=");
|
|
23
|
+
if (eqIdx === -1) continue;
|
|
24
|
+
const key = trimmed.slice(0, eqIdx).trim();
|
|
25
|
+
const value = trimmed.slice(eqIdx + 1).trim();
|
|
26
|
+
envVars[key] = value;
|
|
27
|
+
}
|
|
28
|
+
}
|
|
29
|
+
}
|
|
30
|
+
return envVars;
|
|
31
|
+
}
|
|
32
|
+
var TOKEN_FILE_PATH = join(homedir(), ".config", "prim", "token");
|
|
33
|
+
var REFRESH_TOKEN_PATH = TOKEN_FILE_PATH.replace("/token", "/refresh_token");
|
|
34
|
+
var TOKEN_EXPIRES_PATH = join(homedir(), ".config", "prim", "token_expires_at");
|
|
35
|
+
var DEFAULT_API_URL = "https://api.getprimitive.ai";
|
|
36
|
+
function getSiteUrl() {
|
|
37
|
+
if (process.env.PRIM_API_URL) {
|
|
38
|
+
return process.env.PRIM_API_URL;
|
|
39
|
+
}
|
|
40
|
+
const envVars = loadEnvFile();
|
|
41
|
+
if (envVars.PRIM_API_URL) {
|
|
42
|
+
return envVars.PRIM_API_URL;
|
|
43
|
+
}
|
|
44
|
+
return DEFAULT_API_URL;
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
// src/daemon/client.ts
|
|
48
|
+
import { createConnection } from "net";
|
|
49
|
+
import { homedir as homedir2 } from "os";
|
|
50
|
+
import { join as join2 } from "path";
|
|
51
|
+
var SOCK_PATH = join2(homedir2(), ".config", "prim", "sock");
|
|
52
|
+
var DEFAULT_TIMEOUT_MS = 250;
|
|
53
|
+
var nextRequestId = 1;
|
|
54
|
+
function daemonRequest(method, params = {}, opts = {}) {
|
|
55
|
+
const id = nextRequestId++;
|
|
56
|
+
const request = { id, method, params };
|
|
57
|
+
const timeoutMs = opts.timeoutMs ?? DEFAULT_TIMEOUT_MS;
|
|
58
|
+
return new Promise((resolve3) => {
|
|
59
|
+
let settled = false;
|
|
60
|
+
let socket;
|
|
61
|
+
let buffer = "";
|
|
62
|
+
const settle = (value) => {
|
|
63
|
+
if (settled) {
|
|
64
|
+
return;
|
|
65
|
+
}
|
|
66
|
+
settled = true;
|
|
67
|
+
if (socket) {
|
|
68
|
+
try {
|
|
69
|
+
socket.destroy();
|
|
70
|
+
} catch {
|
|
71
|
+
}
|
|
72
|
+
}
|
|
73
|
+
resolve3(value);
|
|
74
|
+
};
|
|
75
|
+
const timer = setTimeout(() => settle(null), timeoutMs);
|
|
76
|
+
timer.unref();
|
|
77
|
+
try {
|
|
78
|
+
socket = createConnection(SOCK_PATH);
|
|
79
|
+
} catch {
|
|
80
|
+
clearTimeout(timer);
|
|
81
|
+
settle(null);
|
|
82
|
+
return;
|
|
83
|
+
}
|
|
84
|
+
socket.on("error", () => {
|
|
85
|
+
clearTimeout(timer);
|
|
86
|
+
settle(null);
|
|
87
|
+
});
|
|
88
|
+
socket.on("connect", () => {
|
|
89
|
+
try {
|
|
90
|
+
socket?.write(`${JSON.stringify(request)}
|
|
91
|
+
`);
|
|
92
|
+
} catch {
|
|
93
|
+
clearTimeout(timer);
|
|
94
|
+
settle(null);
|
|
95
|
+
}
|
|
96
|
+
});
|
|
97
|
+
socket.on("data", (chunk) => {
|
|
98
|
+
buffer += chunk.toString("utf-8");
|
|
99
|
+
const newlineIdx = buffer.indexOf("\n");
|
|
100
|
+
if (newlineIdx === -1) {
|
|
101
|
+
return;
|
|
102
|
+
}
|
|
103
|
+
const line = buffer.slice(0, newlineIdx);
|
|
104
|
+
try {
|
|
105
|
+
const res = JSON.parse(line);
|
|
106
|
+
if (res.id !== id) {
|
|
107
|
+
clearTimeout(timer);
|
|
108
|
+
settle(null);
|
|
109
|
+
return;
|
|
110
|
+
}
|
|
111
|
+
clearTimeout(timer);
|
|
112
|
+
settle(res.ok && res.result !== void 0 ? res.result : null);
|
|
113
|
+
} catch {
|
|
114
|
+
clearTimeout(timer);
|
|
115
|
+
settle(null);
|
|
116
|
+
}
|
|
117
|
+
});
|
|
118
|
+
socket.on("end", () => {
|
|
119
|
+
if (!settled) {
|
|
120
|
+
clearTimeout(timer);
|
|
121
|
+
settle(null);
|
|
122
|
+
}
|
|
123
|
+
});
|
|
124
|
+
});
|
|
125
|
+
}
|
|
126
|
+
|
|
127
|
+
// src/lib/bin-cache.ts
|
|
128
|
+
import { chmodSync, mkdirSync, renameSync, writeFileSync as writeFileSync2 } from "fs";
|
|
129
|
+
import { homedir as homedir3 } from "os";
|
|
130
|
+
import { join as join4 } from "path";
|
|
131
|
+
|
|
132
|
+
// src/lib/bin-path.ts
|
|
133
|
+
import { existsSync as existsSync2, readFileSync as readFileSync2 } from "fs";
|
|
134
|
+
import { dirname, isAbsolute, join as join3 } from "path";
|
|
135
|
+
import { fileURLToPath } from "url";
|
|
136
|
+
var PKG_NAME = "@primitive.ai/prim";
|
|
137
|
+
var NPX_FALLBACK = `npx --yes -p ${PKG_NAME}@latest`;
|
|
138
|
+
|
|
139
|
+
// src/lib/ansi.ts
|
|
140
|
+
function stripControlChars(text) {
|
|
141
|
+
let out = "";
|
|
142
|
+
for (const ch of text) {
|
|
143
|
+
const code = ch.codePointAt(0) ?? 0;
|
|
144
|
+
if (code > 31 && !(code >= 127 && code <= 159)) {
|
|
145
|
+
out += ch;
|
|
146
|
+
}
|
|
147
|
+
}
|
|
148
|
+
return out;
|
|
149
|
+
}
|
|
150
|
+
|
|
151
|
+
// src/lib/presence.ts
|
|
152
|
+
var DECISION_ORIGIN = "https://app.getprimitive.ai";
|
|
153
|
+
function validDecisionUrl(value) {
|
|
154
|
+
if (typeof value !== "string" || stripControlChars(value) !== value) {
|
|
155
|
+
return void 0;
|
|
156
|
+
}
|
|
157
|
+
try {
|
|
158
|
+
const url = new URL(value);
|
|
159
|
+
if (url.origin !== DECISION_ORIGIN || url.username !== "" || url.password !== "" || url.search !== "" || url.hash !== "" || !/^\/decisions\/[^/]+$/u.test(url.pathname)) {
|
|
160
|
+
return void 0;
|
|
161
|
+
}
|
|
162
|
+
return url.href;
|
|
163
|
+
} catch {
|
|
164
|
+
return void 0;
|
|
165
|
+
}
|
|
166
|
+
}
|
|
167
|
+
function decisionLink(label, decisionUrl) {
|
|
168
|
+
const url = validDecisionUrl(decisionUrl);
|
|
169
|
+
return url ? `\x1B]8;;${url}\x07${label}\x1B]8;;\x07` : label;
|
|
170
|
+
}
|
|
171
|
+
function formatLabeled(labels, cap) {
|
|
172
|
+
if (labels === void 0) {
|
|
173
|
+
return "\u2014";
|
|
174
|
+
}
|
|
175
|
+
if (labels.length === 0) {
|
|
176
|
+
return "just you";
|
|
177
|
+
}
|
|
178
|
+
if (labels.length <= cap) {
|
|
179
|
+
return labels.join(", ");
|
|
180
|
+
}
|
|
181
|
+
return `${labels.slice(0, cap).join(", ")} +${String(labels.length - cap)}`;
|
|
182
|
+
}
|
|
183
|
+
function formatTeammates(names, cap) {
|
|
184
|
+
return formatLabeled(names, cap);
|
|
185
|
+
}
|
|
186
|
+
function formatTeammatesWithArea(teammates, cap) {
|
|
187
|
+
return formatLabeled(
|
|
188
|
+
teammates?.map((t) => {
|
|
189
|
+
const area = t.area?.trim();
|
|
190
|
+
const label = area ? `${t.name} - ${area}` : t.name;
|
|
191
|
+
return decisionLink(label, t.decisionUrl);
|
|
192
|
+
}),
|
|
193
|
+
cap
|
|
194
|
+
);
|
|
195
|
+
}
|
|
196
|
+
|
|
197
|
+
// src/commands/statusline.ts
|
|
198
|
+
var STATUSLINE_TIMEOUT_MS = 200;
|
|
199
|
+
var STATUSLINE_NAME_CAP = 3;
|
|
200
|
+
function readPackageVersion() {
|
|
201
|
+
try {
|
|
202
|
+
const here = dirname2(fileURLToPath2(import.meta.url));
|
|
203
|
+
const candidates = [
|
|
204
|
+
// The supervised runtime stages a tiny manifest beside the standalone
|
|
205
|
+
// statusline bundle, so it remains versioned without loading the package.
|
|
206
|
+
resolve2(here, "manifest.json"),
|
|
207
|
+
resolve2(here, "../../package.json"),
|
|
208
|
+
resolve2(here, "../package.json")
|
|
209
|
+
];
|
|
210
|
+
for (const path of candidates) {
|
|
211
|
+
try {
|
|
212
|
+
const pkg = JSON.parse(readFileSync3(path, "utf-8"));
|
|
213
|
+
if (pkg.version) {
|
|
214
|
+
return pkg.version;
|
|
215
|
+
}
|
|
216
|
+
} catch {
|
|
217
|
+
}
|
|
218
|
+
}
|
|
219
|
+
} catch {
|
|
220
|
+
}
|
|
221
|
+
return "0.0.0";
|
|
222
|
+
}
|
|
223
|
+
function debug(msg) {
|
|
224
|
+
if (process.env.PRIM_STATUSLINE_DEBUG === "1") {
|
|
225
|
+
process.stderr.write(`[prim-statusline] ${msg}
|
|
226
|
+
`);
|
|
227
|
+
}
|
|
228
|
+
}
|
|
229
|
+
async function renderStatusline() {
|
|
230
|
+
const version = readPackageVersion();
|
|
231
|
+
const snapshot = await daemonRequest(
|
|
232
|
+
"status_snapshot",
|
|
233
|
+
// callerEnv lets the daemon withhold presence when it is bound to a different
|
|
234
|
+
// deployment than this statusline targets.
|
|
235
|
+
{ callerEnv: getSiteUrl() },
|
|
236
|
+
{ timeoutMs: STATUSLINE_TIMEOUT_MS }
|
|
237
|
+
);
|
|
238
|
+
if (!snapshot) {
|
|
239
|
+
debug("daemon snapshot missing");
|
|
240
|
+
return `primitive ${version} (daemon: down)`;
|
|
241
|
+
}
|
|
242
|
+
if (snapshot.healthy === false) {
|
|
243
|
+
if (snapshot.ingestion?.healthy === false) {
|
|
244
|
+
const pending = snapshot.ingestion.pendingCount;
|
|
245
|
+
const qualifier = snapshot.ingestion.pendingSampled ? "at least " : "";
|
|
246
|
+
return `primitive ${version} (daemon: degraded \xB7 delivery: stalled${typeof pending === "number" ? ` \xB7 ${qualifier}${String(pending)} pending` : ""})`;
|
|
247
|
+
}
|
|
248
|
+
if (snapshot.heartbeat?.healthy === false) {
|
|
249
|
+
return `primitive ${version} (daemon: degraded \xB7 presence: unavailable)`;
|
|
250
|
+
}
|
|
251
|
+
return `primitive ${version} (daemon: starting)`;
|
|
252
|
+
}
|
|
253
|
+
if (snapshot.envMismatch) {
|
|
254
|
+
return `primitive ${version} (daemon: live \xB7 presence: other env)`;
|
|
255
|
+
}
|
|
256
|
+
if (snapshot.presenceStale) {
|
|
257
|
+
return `primitive ${version} (daemon: live \xB7 presence: stale)`;
|
|
258
|
+
}
|
|
259
|
+
let team;
|
|
260
|
+
if (snapshot.onlineTeammates !== void 0) {
|
|
261
|
+
team = `team: ${formatTeammatesWithArea(snapshot.onlineTeammates, STATUSLINE_NAME_CAP)}`;
|
|
262
|
+
} else if (snapshot.onlineNames !== void 0) {
|
|
263
|
+
team = `team: ${formatTeammates(snapshot.onlineNames, STATUSLINE_NAME_CAP)}`;
|
|
264
|
+
} else if (typeof snapshot.onlineCount === "number") {
|
|
265
|
+
team = `team: ${String(snapshot.onlineCount)} online`;
|
|
266
|
+
} else {
|
|
267
|
+
team = "team: \u2014";
|
|
268
|
+
}
|
|
269
|
+
return `primitive ${version} (daemon: live \xB7 ${team})`;
|
|
270
|
+
}
|
|
271
|
+
|
|
272
|
+
// src/statusline-main.ts
|
|
273
|
+
async function main() {
|
|
274
|
+
try {
|
|
275
|
+
process.stdout.write(await renderStatusline());
|
|
276
|
+
} catch {
|
|
277
|
+
process.stdout.write(`primitive ${readPackageVersion()} (daemon: unavailable)`);
|
|
278
|
+
}
|
|
279
|
+
}
|
|
280
|
+
void main();
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@primitive.ai/prim",
|
|
3
|
-
"version": "0.1.0-alpha.
|
|
3
|
+
"version": "0.1.0-alpha.43",
|
|
4
4
|
"description": "CLI for Primitive's decision graph — passive decision capture, conflict gate, and team presence",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"license": "MIT",
|
|
@@ -36,7 +36,8 @@
|
|
|
36
36
|
"prim-post-tool-use": "dist/hooks/post-tool-use.js",
|
|
37
37
|
"prim-session-start": "dist/hooks/session-start.js",
|
|
38
38
|
"prim-session-end": "dist/hooks/session-end.js",
|
|
39
|
-
"prim-daemon-server": "dist/daemon/server.js"
|
|
39
|
+
"prim-daemon-server": "dist/daemon/server.js",
|
|
40
|
+
"prim-statusline": "dist/statusline-main.js"
|
|
40
41
|
},
|
|
41
42
|
"main": "./dist/index.js",
|
|
42
43
|
"files": [
|
|
@@ -46,9 +47,9 @@
|
|
|
46
47
|
"SKILL.md"
|
|
47
48
|
],
|
|
48
49
|
"scripts": {
|
|
49
|
-
"build": "tsup src/index.ts src/hooks/pre-commit.ts src/hooks/post-commit.ts src/hooks/prim-hook.ts src/hooks/pre-tool-use.ts src/hooks/post-tool-use.ts src/hooks/session-start.ts src/hooks/session-end.ts src/daemon/server.ts --format esm --
|
|
50
|
-
"postbuild": "chmod +x ./dist/index.js ./dist/hooks/pre-commit.js ./dist/hooks/post-commit.js ./dist/hooks/prim-hook.js ./dist/hooks/pre-tool-use.js ./dist/hooks/post-tool-use.js ./dist/hooks/session-start.js ./dist/hooks/session-end.js ./dist/daemon/server.js",
|
|
51
|
-
"dev": "tsup src/index.ts src/hooks/pre-commit.ts src/hooks/post-commit.ts src/hooks/prim-hook.ts src/hooks/pre-tool-use.ts src/hooks/post-tool-use.ts src/hooks/session-start.ts src/hooks/session-end.ts src/daemon/server.ts --format esm --watch --clean",
|
|
50
|
+
"build": "tsup src/index.ts src/hooks/pre-commit.ts src/hooks/post-commit.ts src/hooks/prim-hook.ts src/hooks/pre-tool-use.ts src/hooks/post-tool-use.ts src/hooks/session-start.ts src/hooks/session-end.ts --format esm --clean && tsup src/statusline-main.ts src/daemon/server.ts --format esm --splitting false",
|
|
51
|
+
"postbuild": "chmod +x ./dist/index.js ./dist/statusline-main.js ./dist/hooks/pre-commit.js ./dist/hooks/post-commit.js ./dist/hooks/prim-hook.js ./dist/hooks/pre-tool-use.js ./dist/hooks/post-tool-use.js ./dist/hooks/session-start.js ./dist/hooks/session-end.js ./dist/daemon/server.js",
|
|
52
|
+
"dev": "tsup src/index.ts src/statusline-main.ts src/hooks/pre-commit.ts src/hooks/post-commit.ts src/hooks/prim-hook.ts src/hooks/pre-tool-use.ts src/hooks/post-tool-use.ts src/hooks/session-start.ts src/hooks/session-end.ts src/daemon/server.ts --format esm --watch --clean --splitting false",
|
|
52
53
|
"clean": "rm -rf dist coverage",
|
|
53
54
|
"lint": "biome check src/",
|
|
54
55
|
"format": "biome check --fix src/",
|
package/dist/chunk-LUPD2JSH.js
DELETED
|
@@ -1,78 +0,0 @@
|
|
|
1
|
-
// src/lib/activation.ts
|
|
2
|
-
import { execFileSync as execFileSync2 } from "child_process";
|
|
3
|
-
import { existsSync, readFileSync } from "fs";
|
|
4
|
-
import { homedir } from "os";
|
|
5
|
-
import { join } from "path";
|
|
6
|
-
|
|
7
|
-
// src/lib/git.ts
|
|
8
|
-
import { execFileSync } from "child_process";
|
|
9
|
-
function gitToplevel(cwd) {
|
|
10
|
-
try {
|
|
11
|
-
return execFileSync("git", ["rev-parse", "--show-toplevel"], {
|
|
12
|
-
cwd,
|
|
13
|
-
encoding: "utf-8",
|
|
14
|
-
stdio: ["ignore", "pipe", "ignore"]
|
|
15
|
-
}).trim();
|
|
16
|
-
} catch {
|
|
17
|
-
return null;
|
|
18
|
-
}
|
|
19
|
-
}
|
|
20
|
-
|
|
21
|
-
// src/lib/activation.ts
|
|
22
|
-
var PRIM_ACTIVE_KEY = "prim.active";
|
|
23
|
-
var PROJECT_INSTALL_FILES = [".claude/settings.json", ".codex/hooks.json"];
|
|
24
|
-
var PRIM_HOOK_BINS = ["prim-hook", "prim-pre-tool-use", "prim-post-tool-use"];
|
|
25
|
-
function repoActiveFlag(cwd) {
|
|
26
|
-
try {
|
|
27
|
-
const value = execFileSync2("git", ["config", "--get", PRIM_ACTIVE_KEY], {
|
|
28
|
-
cwd,
|
|
29
|
-
encoding: "utf-8",
|
|
30
|
-
stdio: ["ignore", "pipe", "ignore"]
|
|
31
|
-
}).trim();
|
|
32
|
-
if (value === "true") return "true";
|
|
33
|
-
if (value === "false") return "false";
|
|
34
|
-
return void 0;
|
|
35
|
-
} catch {
|
|
36
|
-
return void 0;
|
|
37
|
-
}
|
|
38
|
-
}
|
|
39
|
-
function setRepoActive(cwd, active) {
|
|
40
|
-
execFileSync2("git", ["config", "--local", PRIM_ACTIVE_KEY, active ? "true" : "false"], {
|
|
41
|
-
cwd,
|
|
42
|
-
stdio: ["ignore", "ignore", "pipe"]
|
|
43
|
-
});
|
|
44
|
-
}
|
|
45
|
-
function activateRepoBestEffort(cwd) {
|
|
46
|
-
try {
|
|
47
|
-
setRepoActive(cwd, true);
|
|
48
|
-
} catch {
|
|
49
|
-
}
|
|
50
|
-
}
|
|
51
|
-
function hasProjectPrimInstall(cwd) {
|
|
52
|
-
const root = gitToplevel(cwd);
|
|
53
|
-
if (root === null || root === homedir()) return false;
|
|
54
|
-
for (const rel of PROJECT_INSTALL_FILES) {
|
|
55
|
-
const path = join(root, rel);
|
|
56
|
-
if (existsSync(path)) {
|
|
57
|
-
try {
|
|
58
|
-
const content = readFileSync(path, "utf-8");
|
|
59
|
-
if (PRIM_HOOK_BINS.some((bin) => content.includes(bin))) return true;
|
|
60
|
-
} catch {
|
|
61
|
-
}
|
|
62
|
-
}
|
|
63
|
-
}
|
|
64
|
-
return false;
|
|
65
|
-
}
|
|
66
|
-
function isRepoActiveForCapture(cwd) {
|
|
67
|
-
const flag = repoActiveFlag(cwd);
|
|
68
|
-
if (flag === "true") return true;
|
|
69
|
-
if (flag === "false") return false;
|
|
70
|
-
return hasProjectPrimInstall(cwd);
|
|
71
|
-
}
|
|
72
|
-
|
|
73
|
-
export {
|
|
74
|
-
gitToplevel,
|
|
75
|
-
setRepoActive,
|
|
76
|
-
activateRepoBestEffort,
|
|
77
|
-
isRepoActiveForCapture
|
|
78
|
-
};
|