@primitive.ai/prim 0.1.0-alpha.67 → 0.1.0-alpha.69
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 +39 -1
- package/SKILL.md +1 -1
- package/dist/{chunk-XPVNH3J6.js → chunk-2KW7DVFJ.js} +121 -130
- package/dist/chunk-3EEFL6NJ.js +529 -0
- package/dist/chunk-4A2XB2PC.js +310 -0
- package/dist/{chunk-G5D3FF3D.js → chunk-BQUJ4AFJ.js} +1 -83
- package/dist/chunk-CUE2LKLR.js +155 -0
- package/dist/chunk-FI3LIRCB.js +262 -0
- package/dist/{chunk-CPSE3Y3T.js → chunk-H64F6QHV.js} +27 -8
- package/dist/chunk-KKP2NYZ3.js +505 -0
- package/dist/{chunk-JQFN6LBO.js → chunk-LEMX3IOG.js} +6 -37
- package/dist/{chunk-U6L2RU67.js → chunk-LMRYZL3D.js} +52 -45
- package/dist/{chunk-XUAP572Z.js → chunk-RO3F6JYV.js} +8 -7
- package/dist/{chunk-TDRXHBEX.js → chunk-TJTVHDJ5.js} +41 -18
- package/dist/{chunk-TCDUH7AN.js → chunk-WB7AUZ4C.js} +17 -0
- package/dist/chunk-XGADQPCI.js +26 -0
- package/dist/{chunk-UN5O5SYG.js → chunk-YWO7O6QG.js} +14 -53
- package/dist/chunk-Z7YPFWAZ.js +917 -0
- package/dist/{chunk-BDGQEJA5.js → chunk-ZOSNEO5N.js} +2 -2
- package/dist/daemon/server.js +11804 -470
- package/dist/hook-runtime/daemon/server.js +13353 -0
- package/dist/hook-runtime/hooks/post-commit.js +1128 -0
- package/dist/hook-runtime/hooks/post-rewrite.js +1148 -0
- package/dist/hook-runtime/hooks/post-tool-use.js +7586 -0
- package/dist/hook-runtime/hooks/pre-commit.js +1314 -0
- package/dist/hook-runtime/hooks/pre-tool-use.js +7099 -0
- package/dist/hook-runtime/hooks/prim-hook.js +7691 -0
- package/dist/hook-runtime/hooks/session-end.js +495 -0
- package/dist/hook-runtime/hooks/session-start.js +11284 -0
- package/dist/hook-runtime/index.js +42775 -0
- package/dist/hook-runtime/statusline-main.js +765 -0
- package/dist/hooks/post-commit.js +7 -4
- package/dist/hooks/post-rewrite.js +7 -4
- package/dist/hooks/post-tool-use.js +64 -32
- package/dist/hooks/pre-commit.js +4 -3
- package/dist/hooks/pre-tool-use.js +118 -38
- package/dist/hooks/prim-hook.js +72 -27
- package/dist/hooks/session-end.js +5 -4
- package/dist/hooks/session-start.js +36 -25
- package/dist/index.js +15039 -2555
- package/dist/statusline-main.js +488 -96
- package/package.json +11 -5
- package/dist/chunk-CZ24W5QU.js +0 -123
- package/dist/chunk-RTLFE6EZ.js +0 -518
- package/dist/chunk-UTKQTZHL.js +0 -88
package/dist/statusline-main.js
CHANGED
|
@@ -3,73 +3,396 @@
|
|
|
3
3
|
// src/commands/statusline.ts
|
|
4
4
|
import { readFileSync as readFileSync5 } from "fs";
|
|
5
5
|
import { dirname as dirname5, resolve as resolve3 } from "path";
|
|
6
|
-
import { fileURLToPath
|
|
6
|
+
import { fileURLToPath } from "url";
|
|
7
7
|
|
|
8
8
|
// src/client.ts
|
|
9
|
-
import { createHash
|
|
9
|
+
import { createHash as createHash2 } from "crypto";
|
|
10
|
+
import { chmodSync, existsSync as existsSync2, mkdirSync as mkdirSync3, readFileSync as readFileSync3, rmSync as rmSync3 } from "fs";
|
|
11
|
+
import { dirname as dirname3 } from "path";
|
|
12
|
+
|
|
13
|
+
// src/lib/atomic-file.ts
|
|
14
|
+
import { randomUUID } from "crypto";
|
|
10
15
|
import {
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
16
|
+
constants,
|
|
17
|
+
closeSync,
|
|
18
|
+
fchmodSync,
|
|
19
|
+
fsyncSync,
|
|
20
|
+
mkdirSync,
|
|
21
|
+
openSync,
|
|
15
22
|
renameSync,
|
|
16
|
-
rmSync
|
|
17
|
-
writeFileSync
|
|
23
|
+
rmSync,
|
|
24
|
+
writeFileSync
|
|
18
25
|
} from "fs";
|
|
26
|
+
import { dirname } from "path";
|
|
27
|
+
|
|
28
|
+
// src/lib/credentials.ts
|
|
29
|
+
import { createHash } from "crypto";
|
|
30
|
+
import { readFileSync } from "fs";
|
|
31
|
+
import { join as join2 } from "path";
|
|
32
|
+
|
|
33
|
+
// src/lib/paths.ts
|
|
19
34
|
import { homedir } from "os";
|
|
20
|
-
import {
|
|
35
|
+
import { isAbsolute, join, normalize } from "path";
|
|
36
|
+
var UNSAFE_CONFIG_PATH_CHARACTERS = /[\p{Cc}\p{Cf}\p{Zl}\p{Zp}]/u;
|
|
37
|
+
function absolutePath(value, variable) {
|
|
38
|
+
if (value !== void 0 && UNSAFE_CONFIG_PATH_CHARACTERS.test(value)) {
|
|
39
|
+
throw new Error(`${variable} contains unsafe characters`);
|
|
40
|
+
}
|
|
41
|
+
if (!value || value.trim() !== value || !isAbsolute(value)) return;
|
|
42
|
+
const normalized = normalize(value);
|
|
43
|
+
return normalized === value ? value : void 0;
|
|
44
|
+
}
|
|
45
|
+
function resolvePrimConfigDirectory(options = {}) {
|
|
46
|
+
const env = options.env ?? process.env;
|
|
47
|
+
const explicit = absolutePath(env.PRIM_CONFIG_DIR, "PRIM_CONFIG_DIR");
|
|
48
|
+
if (explicit) return { path: explicit, source: "explicit" };
|
|
49
|
+
const xdgConfigHome = absolutePath(env.XDG_CONFIG_HOME, "XDG_CONFIG_HOME");
|
|
50
|
+
if (xdgConfigHome) return { path: join(xdgConfigHome, "prim"), source: "xdg" };
|
|
51
|
+
const home = absolutePath(options.homeDir ?? homedir(), "HOME");
|
|
52
|
+
if (!home) {
|
|
53
|
+
throw new Error("cannot resolve Primitive config directory: HOME is not an absolute path");
|
|
54
|
+
}
|
|
55
|
+
return { path: join(home, ".config", "prim"), source: "default" };
|
|
56
|
+
}
|
|
57
|
+
function primConfigDirectory(options = {}) {
|
|
58
|
+
return resolvePrimConfigDirectory(options).path;
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
// src/lib/workos-connect.ts
|
|
62
|
+
var DISCOVERY_MAX_BYTES = 16 * 1024;
|
|
63
|
+
var WORKOS_CONNECT_RESPONSE_MAX_BYTES = 64 * 1024;
|
|
64
|
+
var MAX_DEVICE_LIFETIME_SECONDS = 60 * 60;
|
|
65
|
+
var MAX_TOKEN_LIFETIME_SECONDS = 24 * 60 * 60;
|
|
66
|
+
var MAX_URL_LENGTH = 2048;
|
|
67
|
+
var MAX_CLIENT_ID_LENGTH = 256;
|
|
68
|
+
var CONNECT_CLIENT_ID_PATTERN = /^client_[A-Za-z0-9_-]+$/u;
|
|
69
|
+
var WorkosConnectProtocolError = class extends Error {
|
|
70
|
+
code;
|
|
71
|
+
constructor(code, message) {
|
|
72
|
+
super(message);
|
|
73
|
+
this.name = "WorkosConnectProtocolError";
|
|
74
|
+
this.code = code;
|
|
75
|
+
}
|
|
76
|
+
};
|
|
77
|
+
function recordFrom(value, label) {
|
|
78
|
+
if (value === null || typeof value !== "object" || Array.isArray(value)) {
|
|
79
|
+
throw new WorkosConnectProtocolError("invalid_response", `${label} must be an object`);
|
|
80
|
+
}
|
|
81
|
+
return value;
|
|
82
|
+
}
|
|
83
|
+
function exactString(value, field, maxLength) {
|
|
84
|
+
const containsControl = typeof value === "string" && [...value].some((character) => {
|
|
85
|
+
const code = character.charCodeAt(0);
|
|
86
|
+
return code <= 31 || code === 127;
|
|
87
|
+
});
|
|
88
|
+
if (typeof value !== "string" || value.length === 0 || value.length > maxLength || value.trim() !== value || containsControl) {
|
|
89
|
+
throw new WorkosConnectProtocolError("invalid_response", `${field} is invalid`);
|
|
90
|
+
}
|
|
91
|
+
return value;
|
|
92
|
+
}
|
|
93
|
+
function canonicalIssuer(value) {
|
|
94
|
+
const issuer = exactString(value, "issuer", MAX_URL_LENGTH);
|
|
95
|
+
let parsed;
|
|
96
|
+
try {
|
|
97
|
+
parsed = new URL(issuer);
|
|
98
|
+
} catch {
|
|
99
|
+
throw new WorkosConnectProtocolError("invalid_response", "issuer must be an HTTPS origin");
|
|
100
|
+
}
|
|
101
|
+
if (parsed.protocol !== "https:" || parsed.username || parsed.password || parsed.pathname !== "/" || parsed.search || parsed.hash || parsed.origin !== issuer) {
|
|
102
|
+
throw new WorkosConnectProtocolError("invalid_response", "issuer must be an HTTPS origin");
|
|
103
|
+
}
|
|
104
|
+
return issuer;
|
|
105
|
+
}
|
|
106
|
+
function parseWorkosConnectCredentialMetadata(value) {
|
|
107
|
+
const record = recordFrom(value, "credential metadata");
|
|
108
|
+
if (record.version !== 1 || record.family !== "workos_connect") {
|
|
109
|
+
throw new WorkosConnectProtocolError("invalid_metadata", "credential metadata is invalid");
|
|
110
|
+
}
|
|
111
|
+
const issuer = canonicalIssuer(record.issuer);
|
|
112
|
+
const clientId = exactString(record.clientId, "clientId", MAX_CLIENT_ID_LENGTH);
|
|
113
|
+
const accessTokenHash = exactString(record.accessTokenHash, "accessTokenHash", 64);
|
|
114
|
+
const refreshTokenHash = exactString(record.refreshTokenHash, "refreshTokenHash", 64);
|
|
115
|
+
if (!CONNECT_CLIENT_ID_PATTERN.test(clientId)) {
|
|
116
|
+
throw new WorkosConnectProtocolError("invalid_metadata", "credential clientId is invalid");
|
|
117
|
+
}
|
|
118
|
+
if (!/^[a-f0-9]{64}$/u.test(accessTokenHash)) {
|
|
119
|
+
throw new WorkosConnectProtocolError(
|
|
120
|
+
"invalid_metadata",
|
|
121
|
+
"credential accessTokenHash is invalid"
|
|
122
|
+
);
|
|
123
|
+
}
|
|
124
|
+
if (!/^[a-f0-9]{64}$/u.test(refreshTokenHash)) {
|
|
125
|
+
throw new WorkosConnectProtocolError(
|
|
126
|
+
"invalid_metadata",
|
|
127
|
+
"credential refreshTokenHash is invalid"
|
|
128
|
+
);
|
|
129
|
+
}
|
|
130
|
+
return {
|
|
131
|
+
version: 1,
|
|
132
|
+
family: "workos_connect",
|
|
133
|
+
issuer,
|
|
134
|
+
clientId,
|
|
135
|
+
accessTokenHash,
|
|
136
|
+
refreshTokenHash
|
|
137
|
+
};
|
|
138
|
+
}
|
|
139
|
+
|
|
140
|
+
// src/lib/credentials.ts
|
|
141
|
+
var CONFIG_DIRECTORY = primConfigDirectory();
|
|
142
|
+
var TOKEN_FILE_PATH = join2(CONFIG_DIRECTORY, "token");
|
|
143
|
+
var REFRESH_TOKEN_PATH = join2(CONFIG_DIRECTORY, "refresh_token");
|
|
144
|
+
var TOKEN_EXPIRES_PATH = join2(CONFIG_DIRECTORY, "token_expires_at");
|
|
145
|
+
var TERMINAL_REFRESH_PATH = join2(CONFIG_DIRECTORY, "refresh_terminal");
|
|
146
|
+
var CREDENTIAL_METADATA_PATH = join2(CONFIG_DIRECTORY, "credential_metadata.json");
|
|
147
|
+
var CREDENTIAL_FAMILY_PATH = join2(CONFIG_DIRECTORY, "credential_family.json");
|
|
148
|
+
var CREDENTIAL_MIGRATION_PATH = join2(CONFIG_DIRECTORY, "credential_migration.json");
|
|
149
|
+
var CREDENTIAL_LOCK_PATH = join2(CONFIG_DIRECTORY, "credentials.lock");
|
|
150
|
+
var MAX_CREDENTIAL_METADATA_BYTES = 4096;
|
|
151
|
+
var CREDENTIAL_MIGRATION_VERSION = 1;
|
|
152
|
+
var CREDENTIAL_MIGRATION_STATE = "family_bound";
|
|
153
|
+
function readTrimmed(path) {
|
|
154
|
+
try {
|
|
155
|
+
const value = readFileSync(path, "utf8").trim();
|
|
156
|
+
return value || void 0;
|
|
157
|
+
} catch {
|
|
158
|
+
return void 0;
|
|
159
|
+
}
|
|
160
|
+
}
|
|
161
|
+
function resolveAuthCredential(options = {}) {
|
|
162
|
+
const envToken = (options.env ?? process.env).PRIM_TOKEN?.trim();
|
|
163
|
+
if (envToken) return { token: envToken, source: "environment" };
|
|
164
|
+
const stored = readTrimmed(options.tokenFilePath ?? TOKEN_FILE_PATH);
|
|
165
|
+
if (!stored) return void 0;
|
|
166
|
+
const metadata = readStoredCredentialMetadata(
|
|
167
|
+
options.metadataPath ?? CREDENTIAL_METADATA_PATH,
|
|
168
|
+
options.familyPath ?? CREDENTIAL_FAMILY_PATH,
|
|
169
|
+
options.migrationPath ?? CREDENTIAL_MIGRATION_PATH
|
|
170
|
+
);
|
|
171
|
+
if (metadata.state === "legacy_broker") {
|
|
172
|
+
if (metadata.metadata) {
|
|
173
|
+
const refreshToken2 = readTrimmed(options.refreshTokenPath ?? REFRESH_TOKEN_PATH);
|
|
174
|
+
if (!refreshToken2 || metadata.metadata.accessTokenHash !== credentialFingerprint(stored) || metadata.metadata.refreshTokenHash !== credentialFingerprint(refreshToken2)) {
|
|
175
|
+
return void 0;
|
|
176
|
+
}
|
|
177
|
+
}
|
|
178
|
+
return { token: stored, source: "token_file" };
|
|
179
|
+
}
|
|
180
|
+
if (metadata.state === "invalid") return void 0;
|
|
181
|
+
const refreshToken = readTrimmed(options.refreshTokenPath ?? REFRESH_TOKEN_PATH);
|
|
182
|
+
if (!refreshToken || metadata.metadata.accessTokenHash !== credentialFingerprint(stored) || metadata.metadata.refreshTokenHash !== credentialFingerprint(refreshToken)) {
|
|
183
|
+
return void 0;
|
|
184
|
+
}
|
|
185
|
+
return { token: stored, source: "token_file" };
|
|
186
|
+
}
|
|
187
|
+
function credentialFingerprint(value) {
|
|
188
|
+
return createHash("sha256").update(value).digest("hex");
|
|
189
|
+
}
|
|
190
|
+
function parseLegacyBrokerCredentialMetadata(value) {
|
|
191
|
+
if (value === null || typeof value !== "object" || Array.isArray(value)) {
|
|
192
|
+
throw new Error("credential family is invalid");
|
|
193
|
+
}
|
|
194
|
+
const record = value;
|
|
195
|
+
if (record.version !== 1 || record.family !== "legacy_broker" || typeof record.accessTokenHash !== "string" || typeof record.refreshTokenHash !== "string" || !/^[a-f0-9]{64}$/u.test(record.accessTokenHash) || !/^[a-f0-9]{64}$/u.test(record.refreshTokenHash)) {
|
|
196
|
+
throw new Error("credential family is invalid");
|
|
197
|
+
}
|
|
198
|
+
return {
|
|
199
|
+
version: 1,
|
|
200
|
+
family: "legacy_broker",
|
|
201
|
+
accessTokenHash: record.accessTokenHash,
|
|
202
|
+
refreshTokenHash: record.refreshTokenHash
|
|
203
|
+
};
|
|
204
|
+
}
|
|
205
|
+
function readBoundedCredentialJson(path) {
|
|
206
|
+
let raw;
|
|
207
|
+
try {
|
|
208
|
+
raw = readFileSync(path, "utf8");
|
|
209
|
+
} catch (error) {
|
|
210
|
+
return error.code === "ENOENT" ? void 0 : null;
|
|
211
|
+
}
|
|
212
|
+
if (Buffer.byteLength(raw, "utf8") > MAX_CREDENTIAL_METADATA_BYTES) return null;
|
|
213
|
+
try {
|
|
214
|
+
return JSON.parse(raw);
|
|
215
|
+
} catch {
|
|
216
|
+
return null;
|
|
217
|
+
}
|
|
218
|
+
}
|
|
219
|
+
function readCredentialFamily(path) {
|
|
220
|
+
const parsed = readBoundedCredentialJson(path);
|
|
221
|
+
if (parsed === void 0) return void 0;
|
|
222
|
+
if (parsed === null) return { state: "invalid" };
|
|
223
|
+
try {
|
|
224
|
+
if (typeof parsed === "object" && parsed !== null && !Array.isArray(parsed) && parsed.family === "legacy_broker") {
|
|
225
|
+
return { state: "legacy_broker", metadata: parseLegacyBrokerCredentialMetadata(parsed) };
|
|
226
|
+
}
|
|
227
|
+
return {
|
|
228
|
+
state: "workos_connect",
|
|
229
|
+
metadata: parseWorkosConnectCredentialMetadata(parsed)
|
|
230
|
+
};
|
|
231
|
+
} catch {
|
|
232
|
+
return { state: "invalid" };
|
|
233
|
+
}
|
|
234
|
+
}
|
|
235
|
+
function isCredentialMigrationSentinel(value) {
|
|
236
|
+
if (value === null || typeof value !== "object" || Array.isArray(value)) return false;
|
|
237
|
+
const record = value;
|
|
238
|
+
const keys = Object.keys(record).sort();
|
|
239
|
+
return keys.length === 2 && keys[0] === "state" && keys[1] === "version" && record.version === CREDENTIAL_MIGRATION_VERSION && record.state === CREDENTIAL_MIGRATION_STATE;
|
|
240
|
+
}
|
|
241
|
+
function readCredentialMigrationState(path = CREDENTIAL_MIGRATION_PATH) {
|
|
242
|
+
const parsed = readBoundedCredentialJson(path);
|
|
243
|
+
if (parsed === void 0) return "absent";
|
|
244
|
+
return parsed !== null && isCredentialMigrationSentinel(parsed) ? "valid" : "invalid";
|
|
245
|
+
}
|
|
246
|
+
function readStoredCredentialMetadata(metadataPath = CREDENTIAL_METADATA_PATH, familyPath = CREDENTIAL_FAMILY_PATH, migrationPath = CREDENTIAL_MIGRATION_PATH) {
|
|
247
|
+
const family = readCredentialFamily(familyPath);
|
|
248
|
+
if (family) return family;
|
|
249
|
+
const parsed = readBoundedCredentialJson(metadataPath);
|
|
250
|
+
if (parsed === void 0) {
|
|
251
|
+
return readCredentialMigrationState(migrationPath) === "absent" ? { state: "legacy_broker" } : { state: "invalid" };
|
|
252
|
+
}
|
|
253
|
+
if (parsed === null) return { state: "invalid" };
|
|
254
|
+
try {
|
|
255
|
+
return {
|
|
256
|
+
state: "workos_connect",
|
|
257
|
+
metadata: parseWorkosConnectCredentialMetadata(parsed)
|
|
258
|
+
};
|
|
259
|
+
} catch {
|
|
260
|
+
return { state: "invalid" };
|
|
261
|
+
}
|
|
262
|
+
}
|
|
263
|
+
function decodeJwtPayload(token) {
|
|
264
|
+
const parts = token.split(".");
|
|
265
|
+
if (parts.length !== 3) return void 0;
|
|
266
|
+
try {
|
|
267
|
+
const payload = JSON.parse(Buffer.from(parts[1], "base64url").toString("utf8"));
|
|
268
|
+
if (payload === null || typeof payload !== "object" || Array.isArray(payload)) return void 0;
|
|
269
|
+
return payload;
|
|
270
|
+
} catch {
|
|
271
|
+
return void 0;
|
|
272
|
+
}
|
|
273
|
+
}
|
|
21
274
|
|
|
22
275
|
// src/lib/file-lock.ts
|
|
23
276
|
import { randomBytes } from "crypto";
|
|
24
|
-
import { existsSync, mkdirSync, readFileSync, rmSync, statSync, writeFileSync } from "fs";
|
|
25
|
-
import { dirname, join } from "path";
|
|
277
|
+
import { existsSync, mkdirSync as mkdirSync2, readFileSync as readFileSync2, rmSync as rmSync2, statSync, writeFileSync as writeFileSync2 } from "fs";
|
|
278
|
+
import { dirname as dirname2, join as join3 } from "path";
|
|
279
|
+
var syncSleeper = new Int32Array(new SharedArrayBuffer(Int32Array.BYTES_PER_ELEMENT));
|
|
26
280
|
|
|
27
|
-
// src/
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
281
|
+
// src/lib/terminal-safe.ts
|
|
282
|
+
function replaceIsolatedSurrogates(value) {
|
|
283
|
+
let out = "";
|
|
284
|
+
for (let index = 0; index < value.length; index += 1) {
|
|
285
|
+
const unit = value.charCodeAt(index);
|
|
286
|
+
if (unit >= 55296 && unit <= 56319) {
|
|
287
|
+
const next = value.charCodeAt(index + 1);
|
|
288
|
+
if (next >= 56320 && next <= 57343) {
|
|
289
|
+
out += value[index] + value[index + 1];
|
|
290
|
+
index += 1;
|
|
291
|
+
} else {
|
|
292
|
+
out += "\uFFFD";
|
|
293
|
+
}
|
|
294
|
+
} else if (unit >= 56320 && unit <= 57343) {
|
|
295
|
+
out += "\uFFFD";
|
|
296
|
+
} else {
|
|
297
|
+
out += value[index];
|
|
41
298
|
}
|
|
42
299
|
}
|
|
43
|
-
return
|
|
300
|
+
return out;
|
|
301
|
+
}
|
|
302
|
+
function isUnsafeTerminalCodePoint(point) {
|
|
303
|
+
const c0OrC1 = point <= 31 || point >= 127 && point <= 159;
|
|
304
|
+
const bidiOrZeroWidth = point === 173 || // soft hyphen
|
|
305
|
+
point === 1564 || // Arabic letter mark
|
|
306
|
+
point === 6158 || // Mongolian vowel separator
|
|
307
|
+
point >= 8203 && point <= 8207 || // zero-width + directional marks
|
|
308
|
+
point >= 8234 && point <= 8238 || // bidi embeddings/overrides
|
|
309
|
+
point >= 8288 && point <= 8303 || // word joiner + bidi isolates/controls
|
|
310
|
+
point >= 65529 && point <= 65531 || // interlinear annotation controls
|
|
311
|
+
point === 65279 || // zero-width no-break space / BOM
|
|
312
|
+
point >= 917504 && point <= 917631;
|
|
313
|
+
return c0OrC1 || bidiOrZeroWidth;
|
|
314
|
+
}
|
|
315
|
+
function terminalSafeText(value) {
|
|
316
|
+
return Array.from(replaceIsolatedSurrogates(value)).filter((character) => !isUnsafeTerminalCodePoint(character.codePointAt(0) ?? 0)).join("");
|
|
44
317
|
}
|
|
45
|
-
var
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
var
|
|
49
|
-
var CREDENTIAL_LOCK_PATH = join2(dirname2(TOKEN_FILE_PATH), "credentials.lock");
|
|
318
|
+
var stripControlChars = terminalSafeText;
|
|
319
|
+
|
|
320
|
+
// src/client.ts
|
|
321
|
+
var DEFAULT_API_URL = "https://api.getprimitive.ai";
|
|
50
322
|
function getSiteUrl() {
|
|
51
|
-
return
|
|
323
|
+
return getSiteUrlForEnvironment(process.env.PRIM_API_URL);
|
|
52
324
|
}
|
|
53
|
-
function
|
|
54
|
-
|
|
55
|
-
return loadEnvFile(cwd).PRIM_API_URL ?? DEFAULT_API_URL;
|
|
325
|
+
function getSiteUrlForEnvironment(primApiUrl) {
|
|
326
|
+
return primApiUrl?.trim() || DEFAULT_API_URL;
|
|
56
327
|
}
|
|
328
|
+
var CLI_MANAGEMENT_RESPONSE_MAX_BYTES = 2 * 1024 * 1024;
|
|
57
329
|
|
|
58
330
|
// src/daemon/client.ts
|
|
59
331
|
import { createConnection } from "net";
|
|
60
|
-
import {
|
|
61
|
-
|
|
62
|
-
|
|
332
|
+
import { join as join5 } from "path";
|
|
333
|
+
|
|
334
|
+
// src/daemon/principal.ts
|
|
335
|
+
import { createHash as createHash3 } from "crypto";
|
|
336
|
+
var PRINCIPAL_COMPONENT_MAX_LENGTH = 512;
|
|
337
|
+
function boundedClaim(value) {
|
|
338
|
+
return typeof value === "string" && value.length > 0 && value.length <= PRINCIPAL_COMPONENT_MAX_LENGTH ? value : void 0;
|
|
339
|
+
}
|
|
340
|
+
function sha256(value) {
|
|
341
|
+
return createHash3("sha256").update(value).digest("hex");
|
|
342
|
+
}
|
|
343
|
+
function resolveDaemonPrincipal(token = resolveAuthCredential()?.token) {
|
|
344
|
+
if (!token) return void 0;
|
|
345
|
+
const payload = decodeJwtPayload(token);
|
|
346
|
+
const subject = boundedClaim(payload?.sub);
|
|
347
|
+
const organizationId = boundedClaim(payload?.org_id);
|
|
348
|
+
if (!(subject && organizationId)) return void 0;
|
|
349
|
+
const issuer = boundedClaim(payload?.iss) ?? "unknown-issuer";
|
|
350
|
+
return {
|
|
351
|
+
principalId: sha256(`${issuer}\0${subject}`),
|
|
352
|
+
organizationId,
|
|
353
|
+
credentialFingerprint: sha256(token)
|
|
354
|
+
};
|
|
355
|
+
}
|
|
356
|
+
|
|
357
|
+
// src/daemon/protocol.ts
|
|
358
|
+
var DAEMON_JSON_REQUEST_MAX_BYTES = 64 * 1024;
|
|
359
|
+
var DAEMON_JSON_RESPONSE_MAX_BYTES = 8 * 1024 * 1024;
|
|
360
|
+
function isRecord(value) {
|
|
361
|
+
return typeof value === "object" && value !== null && !Array.isArray(value);
|
|
362
|
+
}
|
|
363
|
+
function parseDaemonResponseEnvelope(value) {
|
|
364
|
+
if (!isRecord(value)) return void 0;
|
|
365
|
+
if (!(typeof value.id === "number" && Number.isSafeInteger(value.id) && value.id >= 0)) {
|
|
366
|
+
return void 0;
|
|
367
|
+
}
|
|
368
|
+
if (typeof value.ok !== "boolean") return void 0;
|
|
369
|
+
if (value.error !== void 0 && typeof value.error !== "string") return void 0;
|
|
370
|
+
return {
|
|
371
|
+
id: value.id,
|
|
372
|
+
ok: value.ok,
|
|
373
|
+
...value.result === void 0 ? {} : { result: value.result },
|
|
374
|
+
...value.error === void 0 ? {} : { error: value.error }
|
|
375
|
+
};
|
|
376
|
+
}
|
|
377
|
+
|
|
378
|
+
// src/daemon/client.ts
|
|
379
|
+
var SOCK_PATH = join5(primConfigDirectory(), "sock");
|
|
63
380
|
var DEFAULT_TIMEOUT_MS = 250;
|
|
64
381
|
var nextRequestId = 1;
|
|
65
382
|
function daemonRequest(method, params = {}, opts = {}) {
|
|
66
383
|
const id = nextRequestId++;
|
|
67
|
-
const
|
|
384
|
+
const caller = resolveDaemonPrincipal();
|
|
385
|
+
const request = {
|
|
386
|
+
id,
|
|
387
|
+
method,
|
|
388
|
+
params,
|
|
389
|
+
...caller ? { caller } : {}
|
|
390
|
+
};
|
|
68
391
|
const timeoutMs = opts.timeoutMs ?? DEFAULT_TIMEOUT_MS;
|
|
69
392
|
return new Promise((resolve4) => {
|
|
70
393
|
let settled = false;
|
|
71
394
|
let socket;
|
|
72
|
-
let buffer =
|
|
395
|
+
let buffer = Buffer.alloc(0);
|
|
73
396
|
const settle = (value) => {
|
|
74
397
|
if (settled) {
|
|
75
398
|
return;
|
|
@@ -106,15 +429,20 @@ function daemonRequest(method, params = {}, opts = {}) {
|
|
|
106
429
|
}
|
|
107
430
|
});
|
|
108
431
|
socket.on("data", (chunk) => {
|
|
109
|
-
buffer
|
|
110
|
-
|
|
432
|
+
buffer = Buffer.concat([buffer, Buffer.from(chunk)]);
|
|
433
|
+
if (buffer.length > DAEMON_JSON_RESPONSE_MAX_BYTES) {
|
|
434
|
+
clearTimeout(timer);
|
|
435
|
+
settle(null);
|
|
436
|
+
return;
|
|
437
|
+
}
|
|
438
|
+
const newlineIdx = buffer.indexOf(10);
|
|
111
439
|
if (newlineIdx === -1) {
|
|
112
440
|
return;
|
|
113
441
|
}
|
|
114
|
-
const line = buffer.
|
|
442
|
+
const line = buffer.subarray(0, newlineIdx).toString("utf8");
|
|
115
443
|
try {
|
|
116
|
-
const res = JSON.parse(line);
|
|
117
|
-
if (res.id !== id) {
|
|
444
|
+
const res = parseDaemonResponseEnvelope(JSON.parse(line));
|
|
445
|
+
if (!res || res.id !== id) {
|
|
118
446
|
clearTimeout(timer);
|
|
119
447
|
settle(null);
|
|
120
448
|
return;
|
|
@@ -137,16 +465,18 @@ function daemonRequest(method, params = {}, opts = {}) {
|
|
|
137
465
|
|
|
138
466
|
// src/lib/activation.ts
|
|
139
467
|
import { execFileSync as execFileSync2 } from "child_process";
|
|
140
|
-
import { existsSync as existsSync3, readFileSync as
|
|
141
|
-
import { homedir as
|
|
142
|
-
import { join as
|
|
468
|
+
import { existsSync as existsSync3, readFileSync as readFileSync4 } from "fs";
|
|
469
|
+
import { homedir as homedir2 } from "os";
|
|
470
|
+
import { join as join6 } from "path";
|
|
143
471
|
|
|
144
472
|
// src/lib/git.ts
|
|
145
473
|
import { execFileSync } from "child_process";
|
|
146
|
-
import { createHash as
|
|
474
|
+
import { createHash as createHash4 } from "crypto";
|
|
147
475
|
import { lstatSync, realpathSync } from "fs";
|
|
148
|
-
import { basename, dirname as
|
|
476
|
+
import { basename, dirname as dirname4, isAbsolute as isAbsolute2, relative, resolve as resolve2, sep } from "path";
|
|
149
477
|
var GIT_TIMEOUT_MS = 1e3;
|
|
478
|
+
var GITHUB_OWNER_PATTERN = /^(?![A-Za-z0-9-]*--)[A-Za-z0-9](?:[A-Za-z0-9-]{0,37}[A-Za-z0-9])?$/u;
|
|
479
|
+
var GITHUB_REPOSITORY_PATTERN = /^[A-Za-z0-9._-]{1,100}$/u;
|
|
150
480
|
function gitToplevel(cwd) {
|
|
151
481
|
try {
|
|
152
482
|
return execFileSync("git", ["rev-parse", "--show-toplevel"], {
|
|
@@ -159,10 +489,41 @@ function gitToplevel(cwd) {
|
|
|
159
489
|
return null;
|
|
160
490
|
}
|
|
161
491
|
}
|
|
492
|
+
function gitValue(cwd, args) {
|
|
493
|
+
try {
|
|
494
|
+
const value = execFileSync("git", args, {
|
|
495
|
+
cwd,
|
|
496
|
+
encoding: "utf-8",
|
|
497
|
+
stdio: ["ignore", "pipe", "ignore"],
|
|
498
|
+
timeout: GIT_TIMEOUT_MS
|
|
499
|
+
}).trim();
|
|
500
|
+
return value || void 0;
|
|
501
|
+
} catch {
|
|
502
|
+
return void 0;
|
|
503
|
+
}
|
|
504
|
+
}
|
|
505
|
+
function githubRepositoryFullName(cwd) {
|
|
506
|
+
const remote = gitValue(cwd, ["config", "--get", "remote.origin.url"]);
|
|
507
|
+
if (!remote || [...remote].some((char) => char < " " || char === "\x7F")) return null;
|
|
508
|
+
const match = /^(?:git@github\.com:([^?#]+)|(?:https|ssh):\/\/(?:[^/@]+@)?github\.com(?::\d+)?\/([^?#]+))$/i.exec(
|
|
509
|
+
remote
|
|
510
|
+
);
|
|
511
|
+
const path = match?.[1] ?? match?.[2];
|
|
512
|
+
if (!path) return null;
|
|
513
|
+
const parts = path.replace(/^\/+|\/+$/g, "").replace(/\.git$/i, "").split("/");
|
|
514
|
+
if (parts.length !== 2 || !GITHUB_OWNER_PATTERN.test(parts[0] ?? "") || !GITHUB_REPOSITORY_PATTERN.test(parts[1] ?? "")) {
|
|
515
|
+
return null;
|
|
516
|
+
}
|
|
517
|
+
return `${parts[0]}/${parts[1]}`;
|
|
518
|
+
}
|
|
162
519
|
|
|
163
520
|
// src/lib/activation.ts
|
|
164
521
|
var PRIM_ACTIVE_KEY = "prim.active";
|
|
522
|
+
var PRIM_REPO_SYNC_ID_KEY = "prim.repoSyncId";
|
|
523
|
+
var PRIM_REPO_SYNC_REPOSITORY_KEY = "prim.repoSyncRepository";
|
|
524
|
+
var PRIM_REPO_BINDING_STATE_KEY = "prim.repoBindingState";
|
|
165
525
|
var GIT_TIMEOUT_MS2 = 1e3;
|
|
526
|
+
var REPO_SYNC_ID_RE = /^[A-Za-z0-9][A-Za-z0-9_-]{0,63}$/;
|
|
166
527
|
var PROJECT_INSTALL_FILES = [".claude/settings.json", ".codex/hooks.json"];
|
|
167
528
|
var PRIM_HOOK_BINS = ["prim-hook", "prim-pre-tool-use", "prim-post-tool-use"];
|
|
168
529
|
function repoActiveFlag(cwd) {
|
|
@@ -180,14 +541,45 @@ function repoActiveFlag(cwd) {
|
|
|
180
541
|
return void 0;
|
|
181
542
|
}
|
|
182
543
|
}
|
|
544
|
+
function isValidRepoSyncId(value) {
|
|
545
|
+
return typeof value === "string" && REPO_SYNC_ID_RE.test(value);
|
|
546
|
+
}
|
|
547
|
+
function localGitConfigValue(cwd, key) {
|
|
548
|
+
try {
|
|
549
|
+
const value = execFileSync2("git", ["config", "--local", "--get", key], {
|
|
550
|
+
cwd,
|
|
551
|
+
encoding: "utf-8",
|
|
552
|
+
stdio: ["ignore", "pipe", "ignore"],
|
|
553
|
+
timeout: GIT_TIMEOUT_MS2
|
|
554
|
+
}).trim();
|
|
555
|
+
return value || void 0;
|
|
556
|
+
} catch {
|
|
557
|
+
return void 0;
|
|
558
|
+
}
|
|
559
|
+
}
|
|
560
|
+
function repoSyncId(cwd) {
|
|
561
|
+
const value = localGitConfigValue(cwd, PRIM_REPO_SYNC_ID_KEY);
|
|
562
|
+
if (!isValidRepoSyncId(value)) return void 0;
|
|
563
|
+
const repositoryFullName = localGitConfigValue(cwd, PRIM_REPO_SYNC_REPOSITORY_KEY);
|
|
564
|
+
return repositoryFullName === githubRepositoryFullName(cwd) ? value : void 0;
|
|
565
|
+
}
|
|
566
|
+
function repositoryBindingState(cwd) {
|
|
567
|
+
const rawId = localGitConfigValue(cwd, PRIM_REPO_SYNC_ID_KEY);
|
|
568
|
+
const marker = localGitConfigValue(cwd, PRIM_REPO_BINDING_STATE_KEY);
|
|
569
|
+
if (rawId !== void 0 && !isValidRepoSyncId(rawId)) return "invalid";
|
|
570
|
+
if (marker === "unbound" || marker === "invalid") return marker;
|
|
571
|
+
if (marker === "connected") return repoSyncId(cwd) ? "connected" : "invalid";
|
|
572
|
+
if (marker !== void 0) return "invalid";
|
|
573
|
+
return rawId === void 0 ? "unbound" : repoSyncId(cwd) ? void 0 : "unbound";
|
|
574
|
+
}
|
|
183
575
|
function hasProjectPrimInstall(cwd) {
|
|
184
576
|
const root = gitToplevel(cwd);
|
|
185
|
-
if (root === null || root ===
|
|
577
|
+
if (root === null || root === homedir2()) return false;
|
|
186
578
|
for (const rel of PROJECT_INSTALL_FILES) {
|
|
187
|
-
const path =
|
|
579
|
+
const path = join6(root, rel);
|
|
188
580
|
if (existsSync3(path)) {
|
|
189
581
|
try {
|
|
190
|
-
const content =
|
|
582
|
+
const content = readFileSync4(path, "utf-8");
|
|
191
583
|
if (PRIM_HOOK_BINS.some((bin) => content.includes(bin))) return true;
|
|
192
584
|
} catch {
|
|
193
585
|
}
|
|
@@ -205,30 +597,6 @@ function decisionIngestionStatus(cwd) {
|
|
|
205
597
|
return isRepoActiveForCapture(cwd) ? "enabled" : "disabled";
|
|
206
598
|
}
|
|
207
599
|
|
|
208
|
-
// src/lib/bin-cache.ts
|
|
209
|
-
import { chmodSync as chmodSync2, mkdirSync as mkdirSync3, renameSync as renameSync2, writeFileSync as writeFileSync3 } from "fs";
|
|
210
|
-
import { homedir as homedir4 } from "os";
|
|
211
|
-
import { join as join6 } from "path";
|
|
212
|
-
|
|
213
|
-
// src/lib/bin-path.ts
|
|
214
|
-
import { existsSync as existsSync4, readFileSync as readFileSync4 } from "fs";
|
|
215
|
-
import { dirname as dirname4, isAbsolute as isAbsolute2, join as join5 } from "path";
|
|
216
|
-
import { fileURLToPath } from "url";
|
|
217
|
-
var PKG_NAME = "@primitive.ai/prim";
|
|
218
|
-
var NPX_FALLBACK = `npx --yes -p ${PKG_NAME}@latest`;
|
|
219
|
-
|
|
220
|
-
// src/lib/ansi.ts
|
|
221
|
-
function stripControlChars(text) {
|
|
222
|
-
let out = "";
|
|
223
|
-
for (const ch of text) {
|
|
224
|
-
const code = ch.codePointAt(0) ?? 0;
|
|
225
|
-
if (code > 31 && !(code >= 127 && code <= 159)) {
|
|
226
|
-
out += ch;
|
|
227
|
-
}
|
|
228
|
-
}
|
|
229
|
-
return out;
|
|
230
|
-
}
|
|
231
|
-
|
|
232
600
|
// src/lib/presence.ts
|
|
233
601
|
var DECISION_ORIGIN = "https://app.getprimitive.ai";
|
|
234
602
|
function validDecisionUrl(value) {
|
|
@@ -245,9 +613,12 @@ function validDecisionUrl(value) {
|
|
|
245
613
|
return void 0;
|
|
246
614
|
}
|
|
247
615
|
}
|
|
248
|
-
function decisionLink(label, decisionUrl) {
|
|
616
|
+
function decisionLink(label, decisionUrl, plain) {
|
|
249
617
|
const url = validDecisionUrl(decisionUrl);
|
|
250
|
-
|
|
618
|
+
if (plain || !url) {
|
|
619
|
+
return label;
|
|
620
|
+
}
|
|
621
|
+
return `\x1B]8;;${url}\x07\x1B[34;4m${label}\x1B[0m\x1B]8;;\x07`;
|
|
251
622
|
}
|
|
252
623
|
function formatLabeled(labels, cap) {
|
|
253
624
|
if (labels === void 0) {
|
|
@@ -264,48 +635,61 @@ function formatLabeled(labels, cap) {
|
|
|
264
635
|
function formatTeammates(names, cap) {
|
|
265
636
|
return formatLabeled(names?.map(stripControlChars), cap);
|
|
266
637
|
}
|
|
267
|
-
function formatTeammatesWithArea(teammates, cap) {
|
|
638
|
+
function formatTeammatesWithArea(teammates, cap, plainLinks = false) {
|
|
268
639
|
return formatLabeled(
|
|
269
640
|
teammates?.map((t) => {
|
|
270
641
|
const name = stripControlChars(t.name);
|
|
271
642
|
const area = stripControlChars(t.area ?? "").trim();
|
|
272
643
|
const label = area ? `${name} - ${area}` : name;
|
|
273
|
-
return decisionLink(label, t.decisionUrl);
|
|
644
|
+
return decisionLink(label, t.decisionUrl, plainLinks);
|
|
274
645
|
}),
|
|
275
646
|
cap
|
|
276
647
|
);
|
|
277
648
|
}
|
|
278
649
|
|
|
279
650
|
// src/lib/statusline-render.ts
|
|
651
|
+
function repositoryBindingDiagnosticLabel(state) {
|
|
652
|
+
if (state === "unbound") return "repository: unbound (enforcement not evaluating)";
|
|
653
|
+
if (state === "invalid") return "repository binding: invalid (run `prim doctor`)";
|
|
654
|
+
return void 0;
|
|
655
|
+
}
|
|
280
656
|
var STATUSLINE_NAME_CAP = 3;
|
|
281
|
-
function formatStatusline(version, snapshot, resolveIngestionStatus) {
|
|
657
|
+
function formatStatusline(version, snapshot, resolveIngestionStatus, options = {}) {
|
|
658
|
+
const ingestionSuffix = (status) => status === void 0 ? "" : ` \xB7 Decision ingestion ${status}`;
|
|
659
|
+
const ingestionStatus = snapshot === null || snapshot.healthy === false ? options.includeIngestionWhenUnavailable ? resolveIngestionStatus() : void 0 : resolveIngestionStatus();
|
|
660
|
+
if (snapshot?.healthy === false && snapshot.needsReauth) {
|
|
661
|
+
return `primitive ${version} (daemon: paused \xB7 run \`prim auth login\`${ingestionSuffix(ingestionStatus)})`;
|
|
662
|
+
}
|
|
663
|
+
const repositoryLabel = repositoryBindingDiagnosticLabel(
|
|
664
|
+
snapshot !== null && snapshot.healthy !== false || options.includeIngestionWhenUnavailable === true ? options.resolveRepositoryBindingState?.() : void 0
|
|
665
|
+
);
|
|
666
|
+
const repositorySuffix = repositoryLabel ? ` \xB7 ${repositoryLabel}` : "";
|
|
282
667
|
if (!snapshot) {
|
|
283
|
-
return `primitive ${version} (daemon: down)`;
|
|
668
|
+
return `primitive ${version} (daemon: down${repositorySuffix}${ingestionSuffix(ingestionStatus)})`;
|
|
284
669
|
}
|
|
285
670
|
if (snapshot.healthy === false) {
|
|
286
|
-
if (snapshot.needsReauth) {
|
|
287
|
-
return `primitive ${version} (daemon: paused \xB7 run \`prim auth login\`)`;
|
|
288
|
-
}
|
|
289
671
|
if (snapshot.ingestion?.healthy === false) {
|
|
290
672
|
const pending = snapshot.ingestion.pendingCount;
|
|
291
673
|
const qualifier = snapshot.ingestion.pendingSampled ? "at least " : "";
|
|
292
|
-
return `primitive ${version} (daemon: degraded \xB7 delivery: stalled${typeof pending === "number" ? ` \xB7 ${qualifier}${String(pending)} pending` : ""})`;
|
|
674
|
+
return `primitive ${version} (daemon: degraded \xB7 delivery: stalled${typeof pending === "number" ? ` \xB7 ${qualifier}${String(pending)} pending` : ""}${repositorySuffix}${ingestionSuffix(ingestionStatus)})`;
|
|
293
675
|
}
|
|
294
676
|
if (snapshot.heartbeat?.healthy === false) {
|
|
295
|
-
return `primitive ${version} (daemon: degraded \xB7 presence: unavailable)`;
|
|
677
|
+
return `primitive ${version} (daemon: degraded \xB7 presence: unavailable${repositorySuffix}${ingestionSuffix(ingestionStatus)})`;
|
|
296
678
|
}
|
|
297
|
-
return `primitive ${version} (daemon: starting)`;
|
|
679
|
+
return `primitive ${version} (daemon: starting${repositorySuffix}${ingestionSuffix(ingestionStatus)})`;
|
|
298
680
|
}
|
|
299
|
-
const ingestionStatus = resolveIngestionStatus();
|
|
300
681
|
if (snapshot.envMismatch) {
|
|
301
|
-
return `primitive ${version} (daemon: live, Decision ingestion ${ingestionStatus} \xB7 presence: other env)`;
|
|
682
|
+
return `primitive ${version} (daemon: live, Decision ingestion ${ingestionStatus}${repositorySuffix} \xB7 presence: other env)`;
|
|
683
|
+
}
|
|
684
|
+
if (snapshot.principalMismatch) {
|
|
685
|
+
return `primitive ${version} (daemon: live, Decision ingestion ${ingestionStatus}${repositorySuffix} \xB7 presence: other account)`;
|
|
302
686
|
}
|
|
303
687
|
if (snapshot.presenceStale) {
|
|
304
|
-
return `primitive ${version} (daemon: live, Decision ingestion ${ingestionStatus} \xB7 presence: stale)`;
|
|
688
|
+
return `primitive ${version} (daemon: live, Decision ingestion ${ingestionStatus}${repositorySuffix} \xB7 presence: stale)`;
|
|
305
689
|
}
|
|
306
690
|
let team;
|
|
307
691
|
if (snapshot.onlineTeammates !== void 0) {
|
|
308
|
-
team = `team: ${formatTeammatesWithArea(snapshot.onlineTeammates, STATUSLINE_NAME_CAP)}`;
|
|
692
|
+
team = `team: ${formatTeammatesWithArea(snapshot.onlineTeammates, STATUSLINE_NAME_CAP, options.plainLinks === true)}`;
|
|
309
693
|
} else if (snapshot.onlineNames !== void 0) {
|
|
310
694
|
team = `team: ${formatTeammates(snapshot.onlineNames, STATUSLINE_NAME_CAP)}`;
|
|
311
695
|
} else if (typeof snapshot.onlineCount === "number") {
|
|
@@ -313,14 +697,14 @@ function formatStatusline(version, snapshot, resolveIngestionStatus) {
|
|
|
313
697
|
} else {
|
|
314
698
|
team = "team: \u2014";
|
|
315
699
|
}
|
|
316
|
-
return `primitive ${version} (daemon: live, Decision ingestion ${ingestionStatus} \xB7 ${team})`;
|
|
700
|
+
return `primitive ${version} (daemon: live, Decision ingestion ${ingestionStatus}${repositorySuffix} \xB7 ${team})`;
|
|
317
701
|
}
|
|
318
702
|
|
|
319
703
|
// src/commands/statusline.ts
|
|
320
704
|
var STATUSLINE_TIMEOUT_MS = 200;
|
|
321
705
|
function readPackageVersion() {
|
|
322
706
|
try {
|
|
323
|
-
const here = dirname5(
|
|
707
|
+
const here = dirname5(fileURLToPath(import.meta.url));
|
|
324
708
|
const candidates = [
|
|
325
709
|
// Schema-v2 runtimes staged a manifest beside this standalone bundle.
|
|
326
710
|
// Keep resolving it while those legacy launchers transition to schema v3.
|
|
@@ -349,6 +733,12 @@ function debug(msg) {
|
|
|
349
733
|
}
|
|
350
734
|
async function renderStatusline() {
|
|
351
735
|
const version = readPackageVersion();
|
|
736
|
+
const cwd = process.cwd();
|
|
737
|
+
let ingestion;
|
|
738
|
+
const resolveIngestion = () => {
|
|
739
|
+
ingestion ??= decisionIngestionStatus(cwd);
|
|
740
|
+
return ingestion;
|
|
741
|
+
};
|
|
352
742
|
const snapshot = await daemonRequest(
|
|
353
743
|
"status_snapshot",
|
|
354
744
|
// callerEnv lets the daemon withhold presence when it is bound to a different
|
|
@@ -359,7 +749,9 @@ async function renderStatusline() {
|
|
|
359
749
|
if (!snapshot) {
|
|
360
750
|
debug("daemon snapshot missing");
|
|
361
751
|
}
|
|
362
|
-
return formatStatusline(version, snapshot,
|
|
752
|
+
return formatStatusline(version, snapshot, resolveIngestion, {
|
|
753
|
+
resolveRepositoryBindingState: () => resolveIngestion() === "enabled" ? repositoryBindingState(cwd) : void 0
|
|
754
|
+
});
|
|
363
755
|
}
|
|
364
756
|
|
|
365
757
|
// src/statusline-main.ts
|