@primitive.ai/prim 0.1.0-alpha.49 → 0.1.0-alpha.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/{chunk-5K725K35.js → chunk-BOTKPLTI.js} +1 -1
- package/dist/{chunk-TPROV45L.js → chunk-DWDEQRWN.js} +12 -1
- package/dist/{chunk-WRPKAPVE.js → chunk-VDLAAUWV.js} +1 -1
- package/dist/{chunk-CK75QBCB.js → chunk-WELBNODJ.js} +1 -1
- package/dist/daemon/server.js +46 -18
- package/dist/hooks/post-commit.js +2 -2
- package/dist/hooks/post-tool-use.js +2 -2
- package/dist/hooks/pre-commit.js +2 -2
- package/dist/hooks/pre-tool-use.js +1 -1
- package/dist/hooks/prim-hook.js +3 -3
- package/dist/hooks/session-start.js +23 -3
- package/dist/index.js +56 -36
- package/dist/statusline-main.js +3 -0
- package/package.json +1 -1
|
@@ -154,6 +154,7 @@ var CONFIG_DIR_MODE = 448;
|
|
|
154
154
|
var CREDENTIAL_FILE_MODE = 384;
|
|
155
155
|
var REFRESH_THRESHOLD_MS = 6e4;
|
|
156
156
|
var DEFAULT_API_URL = "https://api.getprimitive.ai";
|
|
157
|
+
var AUTH_EXPIRED_MESSAGE = "Authentication expired. Run `prim auth login` to re-authenticate.";
|
|
157
158
|
function loadEnvFile() {
|
|
158
159
|
const envVars = {};
|
|
159
160
|
for (const file of [".env.local", ".env"]) {
|
|
@@ -349,6 +350,7 @@ async function performTokenRefresh(options = {}) {
|
|
|
349
350
|
if (selected?.source !== "token_file") return void 0;
|
|
350
351
|
const startingGeneration = readTrimmed(REFRESH_TOKEN_PATH);
|
|
351
352
|
if (!startingGeneration || isSessionEnded()) return void 0;
|
|
353
|
+
const startingAccessToken = selected.token;
|
|
352
354
|
return withCredentialLock(
|
|
353
355
|
async () => {
|
|
354
356
|
const currentCredential = resolveAuthCredential();
|
|
@@ -358,6 +360,9 @@ async function performTokenRefresh(options = {}) {
|
|
|
358
360
|
if (currentGeneration !== startingGeneration) {
|
|
359
361
|
return currentCredential.token;
|
|
360
362
|
}
|
|
363
|
+
if (currentCredential.token !== startingAccessToken) {
|
|
364
|
+
return currentCredential.token;
|
|
365
|
+
}
|
|
361
366
|
if (isSessionEnded()) return void 0;
|
|
362
367
|
const response = await fetch(`${getSiteUrl()}/mcp/broker/refresh`, {
|
|
363
368
|
method: "POST",
|
|
@@ -442,6 +447,12 @@ async function request(method, path, body, options) {
|
|
|
442
447
|
});
|
|
443
448
|
if (token) credential = { token, source: "token_file" };
|
|
444
449
|
}
|
|
450
|
+
if (!credential) {
|
|
451
|
+
throw new HttpError(401, AUTH_EXPIRED_MESSAGE);
|
|
452
|
+
}
|
|
453
|
+
if (isTokenExpiringSoon(credential) && isSessionEnded()) {
|
|
454
|
+
throw new HttpError(401, AUTH_EXPIRED_MESSAGE);
|
|
455
|
+
}
|
|
445
456
|
const doFetch = (token) => {
|
|
446
457
|
const headers = { "Content-Type": "application/json" };
|
|
447
458
|
if (token) headers.Authorization = `Bearer ${token}`;
|
|
@@ -471,7 +482,7 @@ async function request(method, path, body, options) {
|
|
|
471
482
|
}
|
|
472
483
|
if (!response.ok) {
|
|
473
484
|
if (response.status === 401) {
|
|
474
|
-
throw new HttpError(401,
|
|
485
|
+
throw new HttpError(401, AUTH_EXPIRED_MESSAGE);
|
|
475
486
|
}
|
|
476
487
|
const errorBody = await response.json().catch(() => null);
|
|
477
488
|
throw new HttpError(response.status, errorBody?.error ?? `HTTP ${response.status}`);
|
package/dist/daemon/server.js
CHANGED
|
@@ -4,7 +4,7 @@
|
|
|
4
4
|
import { existsSync as existsSync6, readFileSync as readFileSync5, unlinkSync as unlinkSync3 } from "fs";
|
|
5
5
|
import { createServer } from "net";
|
|
6
6
|
import { homedir as homedir5 } from "os";
|
|
7
|
-
import { join as
|
|
7
|
+
import { join as join8 } from "path";
|
|
8
8
|
import { fileURLToPath } from "url";
|
|
9
9
|
|
|
10
10
|
// src/client.ts
|
|
@@ -163,6 +163,7 @@ var CONFIG_DIR_MODE = 448;
|
|
|
163
163
|
var CREDENTIAL_FILE_MODE = 384;
|
|
164
164
|
var REFRESH_THRESHOLD_MS = 6e4;
|
|
165
165
|
var DEFAULT_API_URL = "https://api.getprimitive.ai";
|
|
166
|
+
var AUTH_EXPIRED_MESSAGE = "Authentication expired. Run `prim auth login` to re-authenticate.";
|
|
166
167
|
function loadEnvFile() {
|
|
167
168
|
const envVars = {};
|
|
168
169
|
for (const file of [".env.local", ".env"]) {
|
|
@@ -319,6 +320,7 @@ async function performTokenRefresh(options = {}) {
|
|
|
319
320
|
if (selected?.source !== "token_file") return void 0;
|
|
320
321
|
const startingGeneration = readTrimmed(REFRESH_TOKEN_PATH);
|
|
321
322
|
if (!startingGeneration || isSessionEnded()) return void 0;
|
|
323
|
+
const startingAccessToken = selected.token;
|
|
322
324
|
return withCredentialLock(
|
|
323
325
|
async () => {
|
|
324
326
|
const currentCredential = resolveAuthCredential();
|
|
@@ -328,6 +330,9 @@ async function performTokenRefresh(options = {}) {
|
|
|
328
330
|
if (currentGeneration !== startingGeneration) {
|
|
329
331
|
return currentCredential.token;
|
|
330
332
|
}
|
|
333
|
+
if (currentCredential.token !== startingAccessToken) {
|
|
334
|
+
return currentCredential.token;
|
|
335
|
+
}
|
|
331
336
|
if (isSessionEnded()) return void 0;
|
|
332
337
|
const response = await fetch(`${getSiteUrl()}/mcp/broker/refresh`, {
|
|
333
338
|
method: "POST",
|
|
@@ -412,6 +417,12 @@ async function request(method, path, body, options) {
|
|
|
412
417
|
});
|
|
413
418
|
if (token) credential = { token, source: "token_file" };
|
|
414
419
|
}
|
|
420
|
+
if (!credential) {
|
|
421
|
+
throw new HttpError(401, AUTH_EXPIRED_MESSAGE);
|
|
422
|
+
}
|
|
423
|
+
if (isTokenExpiringSoon(credential) && isSessionEnded()) {
|
|
424
|
+
throw new HttpError(401, AUTH_EXPIRED_MESSAGE);
|
|
425
|
+
}
|
|
415
426
|
const doFetch = (token) => {
|
|
416
427
|
const headers = { "Content-Type": "application/json" };
|
|
417
428
|
if (token) headers.Authorization = `Bearer ${token}`;
|
|
@@ -441,7 +452,7 @@ async function request(method, path, body, options) {
|
|
|
441
452
|
}
|
|
442
453
|
if (!response.ok) {
|
|
443
454
|
if (response.status === 401) {
|
|
444
|
-
throw new HttpError(401,
|
|
455
|
+
throw new HttpError(401, AUTH_EXPIRED_MESSAGE);
|
|
445
456
|
}
|
|
446
457
|
const errorBody = await response.json().catch(() => null);
|
|
447
458
|
throw new HttpError(response.status, errorBody?.error ?? `HTTP ${response.status}`);
|
|
@@ -457,6 +468,7 @@ function getClient() {
|
|
|
457
468
|
|
|
458
469
|
// src/flusher.ts
|
|
459
470
|
import { createReadStream, renameSync as renameSync2, unlinkSync } from "fs";
|
|
471
|
+
import { dirname as dirname4, join as join4 } from "path";
|
|
460
472
|
import { createInterface } from "readline";
|
|
461
473
|
|
|
462
474
|
// src/ingest-response.ts
|
|
@@ -824,11 +836,23 @@ async function flushOnce() {
|
|
|
824
836
|
return { flushed: total };
|
|
825
837
|
}
|
|
826
838
|
var flushInFlight;
|
|
839
|
+
var FLUSH_LOCK_PATH = join4(dirname4(JOURNAL_DIR), ".flush.lock");
|
|
840
|
+
var FLUSH_LOCK_TIMEOUT_MS = 250;
|
|
841
|
+
function isFlushLockContended(error) {
|
|
842
|
+
return error instanceof Error && error.message.startsWith("timed out waiting for file lock");
|
|
843
|
+
}
|
|
827
844
|
function flush() {
|
|
828
845
|
if (flushInFlight) {
|
|
829
846
|
return flushInFlight;
|
|
830
847
|
}
|
|
831
|
-
const attempt =
|
|
848
|
+
const attempt = withFileLock(FLUSH_LOCK_PATH, flushOnce, {
|
|
849
|
+
timeoutMs: FLUSH_LOCK_TIMEOUT_MS
|
|
850
|
+
}).catch((error) => {
|
|
851
|
+
if (isFlushLockContended(error)) {
|
|
852
|
+
return { flushed: 0, skipped: true };
|
|
853
|
+
}
|
|
854
|
+
throw error;
|
|
855
|
+
}).finally(() => {
|
|
832
856
|
if (flushInFlight === attempt) {
|
|
833
857
|
flushInFlight = void 0;
|
|
834
858
|
}
|
|
@@ -840,8 +864,8 @@ function flush() {
|
|
|
840
864
|
// src/daemon/client.ts
|
|
841
865
|
import { createConnection } from "net";
|
|
842
866
|
import { homedir as homedir3 } from "os";
|
|
843
|
-
import { join as
|
|
844
|
-
var SOCK_PATH =
|
|
867
|
+
import { join as join5 } from "path";
|
|
868
|
+
var SOCK_PATH = join5(homedir3(), ".config", "prim", "sock");
|
|
845
869
|
var DEFAULT_TIMEOUT_MS2 = 250;
|
|
846
870
|
var nextRequestId = 1;
|
|
847
871
|
function daemonRequest(method, params = {}, opts = {}) {
|
|
@@ -936,7 +960,7 @@ function assertCallerEnvMatches(callerEnv, boundEnv) {
|
|
|
936
960
|
// src/daemon/health.ts
|
|
937
961
|
import { chmodSync as chmodSync2, existsSync as existsSync4, mkdirSync as mkdirSync4, renameSync as renameSync3, writeFileSync as writeFileSync3 } from "fs";
|
|
938
962
|
import { homedir as homedir4 } from "os";
|
|
939
|
-
import { dirname as
|
|
963
|
+
import { dirname as dirname5, join as join6 } from "path";
|
|
940
964
|
var CONFIG_DIR_MODE2 = 448;
|
|
941
965
|
var HEALTH_FILE_MODE = 384;
|
|
942
966
|
var INGESTION_SLA_MS = 3e4;
|
|
@@ -944,7 +968,7 @@ var HEARTBEAT_FRESH_MS = 9e4;
|
|
|
944
968
|
var HEARTBEAT_RETRY_CAP_MS = 3e4;
|
|
945
969
|
var INGESTION_RETRY_BASE_MS = 5e3;
|
|
946
970
|
var INGESTION_RETRY_CAP_MS = 15e3;
|
|
947
|
-
var DAEMON_HEALTH_PATH =
|
|
971
|
+
var DAEMON_HEALTH_PATH = join6(homedir4(), ".config", "prim", "daemon-health.json");
|
|
948
972
|
function createDaemonHealthState(version, pid, startedAt2) {
|
|
949
973
|
return {
|
|
950
974
|
schemaVersion: 1,
|
|
@@ -982,11 +1006,11 @@ function ingestionRetryDelayMs(consecutiveFailures, random = Math.random) {
|
|
|
982
1006
|
return retryDelayMs(consecutiveFailures, INGESTION_RETRY_CAP_MS, random);
|
|
983
1007
|
}
|
|
984
1008
|
function writeDaemonHealthState(state, path = DAEMON_HEALTH_PATH) {
|
|
985
|
-
const dir =
|
|
1009
|
+
const dir = dirname5(path);
|
|
986
1010
|
if (!existsSync4(dir)) {
|
|
987
1011
|
mkdirSync4(dir, { recursive: true, mode: CONFIG_DIR_MODE2 });
|
|
988
1012
|
}
|
|
989
|
-
const tmp =
|
|
1013
|
+
const tmp = join6(dir, `.${process.pid}.daemon-health.tmp`);
|
|
990
1014
|
writeFileSync3(tmp, `${JSON.stringify(state, null, 2)}
|
|
991
1015
|
`, { mode: HEALTH_FILE_MODE });
|
|
992
1016
|
chmodSync2(tmp, HEALTH_FILE_MODE);
|
|
@@ -1006,7 +1030,7 @@ import {
|
|
|
1006
1030
|
unlinkSync as unlinkSync2,
|
|
1007
1031
|
writeFileSync as writeFileSync4
|
|
1008
1032
|
} from "fs";
|
|
1009
|
-
import { join as
|
|
1033
|
+
import { join as join7 } from "path";
|
|
1010
1034
|
var DIR_MODE = 448;
|
|
1011
1035
|
var FILE_MODE2 = 384;
|
|
1012
1036
|
var DAEMON_STARTUP_GRACE_MS = 5e3;
|
|
@@ -1037,10 +1061,10 @@ function daemonProcessIsAlive(pid) {
|
|
|
1037
1061
|
}
|
|
1038
1062
|
}
|
|
1039
1063
|
function readDaemonOwner(configDir) {
|
|
1040
|
-
return readLockRecord(
|
|
1064
|
+
return readLockRecord(join7(configDir, "daemon.lock", "owner.json"));
|
|
1041
1065
|
}
|
|
1042
1066
|
function readDaemonPid(configDir) {
|
|
1043
|
-
const path =
|
|
1067
|
+
const path = join7(configDir, "daemon.pid");
|
|
1044
1068
|
const pid = numericPid(path);
|
|
1045
1069
|
if (pid === void 0) {
|
|
1046
1070
|
return;
|
|
@@ -1055,9 +1079,9 @@ function acquireDaemonOwnership(configDir, opts = {}) {
|
|
|
1055
1079
|
const pid = opts.pid ?? process.pid;
|
|
1056
1080
|
const now = opts.now ?? Date.now();
|
|
1057
1081
|
const isAlive = opts.isAlive ?? daemonProcessIsAlive;
|
|
1058
|
-
const lockDir =
|
|
1059
|
-
const ownerPath2 =
|
|
1060
|
-
const pidPath =
|
|
1082
|
+
const lockDir = join7(configDir, "daemon.lock");
|
|
1083
|
+
const ownerPath2 = join7(lockDir, "owner.json");
|
|
1084
|
+
const pidPath = join7(configDir, "daemon.pid");
|
|
1061
1085
|
const instanceId = randomUUID();
|
|
1062
1086
|
if (!existsSync5(configDir)) {
|
|
1063
1087
|
mkdirSync5(configDir, { recursive: true, mode: DIR_MODE });
|
|
@@ -1155,8 +1179,8 @@ function releaseDaemonOwnership(ownership2, socketPath) {
|
|
|
1155
1179
|
}
|
|
1156
1180
|
|
|
1157
1181
|
// src/daemon/server.ts
|
|
1158
|
-
var CONFIG_DIR =
|
|
1159
|
-
var SOCK_PATH2 =
|
|
1182
|
+
var CONFIG_DIR = join8(homedir5(), ".config", "prim");
|
|
1183
|
+
var SOCK_PATH2 = join8(CONFIG_DIR, "sock");
|
|
1160
1184
|
var HEARTBEAT_INTERVAL_MS = 3e4;
|
|
1161
1185
|
var INGESTION_POLL_INTERVAL_MS = 15e3;
|
|
1162
1186
|
var TOKEN_CHECK_INTERVAL_MS = 6e4;
|
|
@@ -1190,7 +1214,7 @@ function resolveRuntimeVersion() {
|
|
|
1190
1214
|
}
|
|
1191
1215
|
try {
|
|
1192
1216
|
const here = fileURLToPath(new URL(".", import.meta.url));
|
|
1193
|
-
const pkg = JSON.parse(readFileSync5(
|
|
1217
|
+
const pkg = JSON.parse(readFileSync5(join8(here, "..", "..", "package.json"), "utf-8"));
|
|
1194
1218
|
return pkg.version ?? "unknown";
|
|
1195
1219
|
} catch {
|
|
1196
1220
|
return "unknown";
|
|
@@ -1430,6 +1454,10 @@ async function runIngestionLoop() {
|
|
|
1430
1454
|
persistHealth();
|
|
1431
1455
|
try {
|
|
1432
1456
|
const result = await flush();
|
|
1457
|
+
if (result.skipped) {
|
|
1458
|
+
scheduleIngestion(INGESTION_POLL_INTERVAL_MS);
|
|
1459
|
+
return;
|
|
1460
|
+
}
|
|
1433
1461
|
daemonHealth.ingestion.lastSuccessAt = Date.now();
|
|
1434
1462
|
daemonHealth.ingestion.lastAcknowledgedCount = result.flushed;
|
|
1435
1463
|
daemonHealth.ingestion.consecutiveFailures = 0;
|
|
@@ -5,11 +5,11 @@ import {
|
|
|
5
5
|
import {
|
|
6
6
|
appendMove,
|
|
7
7
|
resolveOrg
|
|
8
|
-
} from "../chunk-
|
|
8
|
+
} from "../chunk-BOTKPLTI.js";
|
|
9
9
|
import {
|
|
10
10
|
isRepoActiveForCapture
|
|
11
11
|
} from "../chunk-R5ZJRSLV.js";
|
|
12
|
-
import "../chunk-
|
|
12
|
+
import "../chunk-DWDEQRWN.js";
|
|
13
13
|
|
|
14
14
|
// src/hooks/post-commit.ts
|
|
15
15
|
import { execSync, spawn } from "child_process";
|
|
@@ -13,7 +13,7 @@ import {
|
|
|
13
13
|
import {
|
|
14
14
|
appendMove,
|
|
15
15
|
resolveOrg
|
|
16
|
-
} from "../chunk-
|
|
16
|
+
} from "../chunk-BOTKPLTI.js";
|
|
17
17
|
import {
|
|
18
18
|
getOrCreateWorkspaceId
|
|
19
19
|
} from "../chunk-IMAIBPUC.js";
|
|
@@ -25,7 +25,7 @@ import {
|
|
|
25
25
|
} from "../chunk-R5ZJRSLV.js";
|
|
26
26
|
import {
|
|
27
27
|
getClient
|
|
28
|
-
} from "../chunk-
|
|
28
|
+
} from "../chunk-DWDEQRWN.js";
|
|
29
29
|
import {
|
|
30
30
|
normalizeEnvelope,
|
|
31
31
|
parseAgent
|
package/dist/hooks/pre-commit.js
CHANGED
package/dist/hooks/prim-hook.js
CHANGED
|
@@ -9,7 +9,7 @@ import {
|
|
|
9
9
|
import {
|
|
10
10
|
appendMove,
|
|
11
11
|
resolveOrg
|
|
12
|
-
} from "../chunk-
|
|
12
|
+
} from "../chunk-BOTKPLTI.js";
|
|
13
13
|
import {
|
|
14
14
|
buildHookOutput,
|
|
15
15
|
handoffHookOutput
|
|
@@ -19,7 +19,7 @@ import {
|
|
|
19
19
|
acknowledgeDecisionFeedback,
|
|
20
20
|
leaseDecisionFeedback,
|
|
21
21
|
renderFeedback
|
|
22
|
-
} from "../chunk-
|
|
22
|
+
} from "../chunk-WELBNODJ.js";
|
|
23
23
|
import {
|
|
24
24
|
getOrCreateWorkspaceId
|
|
25
25
|
} from "../chunk-IMAIBPUC.js";
|
|
@@ -29,7 +29,7 @@ import {
|
|
|
29
29
|
import {
|
|
30
30
|
isRepoActiveForCapture
|
|
31
31
|
} from "../chunk-R5ZJRSLV.js";
|
|
32
|
-
import "../chunk-
|
|
32
|
+
import "../chunk-DWDEQRWN.js";
|
|
33
33
|
import {
|
|
34
34
|
normalizeEnvelope,
|
|
35
35
|
parseAgent
|
|
@@ -8,7 +8,7 @@ import {
|
|
|
8
8
|
acknowledgeDecisionFeedback,
|
|
9
9
|
leaseDecisionFeedback,
|
|
10
10
|
renderFeedback
|
|
11
|
-
} from "../chunk-
|
|
11
|
+
} from "../chunk-WELBNODJ.js";
|
|
12
12
|
import {
|
|
13
13
|
getOrCreateWorkspaceId
|
|
14
14
|
} from "../chunk-IMAIBPUC.js";
|
|
@@ -20,8 +20,9 @@ import {
|
|
|
20
20
|
isRepoActiveForCapture
|
|
21
21
|
} from "../chunk-R5ZJRSLV.js";
|
|
22
22
|
import {
|
|
23
|
-
getSiteUrl
|
|
24
|
-
|
|
23
|
+
getSiteUrl,
|
|
24
|
+
isSessionEnded
|
|
25
|
+
} from "../chunk-DWDEQRWN.js";
|
|
25
26
|
import {
|
|
26
27
|
daemonRequest
|
|
27
28
|
} from "../chunk-UTKQTZHL.js";
|
|
@@ -50,6 +51,18 @@ function kickDaemonEnsure(options = {}) {
|
|
|
50
51
|
}
|
|
51
52
|
}
|
|
52
53
|
|
|
54
|
+
// src/hooks/reauth-notice.ts
|
|
55
|
+
var REAUTH_NOTICE = "[prim] authentication ended \u2014 run `prim auth login` to resume decision capture";
|
|
56
|
+
function reauthNoticeOutput(agent) {
|
|
57
|
+
if (agent === "codex") {
|
|
58
|
+
return buildHookOutput({ additionalContext: REAUTH_NOTICE });
|
|
59
|
+
}
|
|
60
|
+
if (agent === "claude_code") {
|
|
61
|
+
return buildHookOutput({ systemMessage: REAUTH_NOTICE });
|
|
62
|
+
}
|
|
63
|
+
return;
|
|
64
|
+
}
|
|
65
|
+
|
|
53
66
|
// src/hooks/session-start.ts
|
|
54
67
|
var STDIN_TIMEOUT_MS = 1e3;
|
|
55
68
|
var DAEMON_TIMEOUT_MS = 250;
|
|
@@ -110,6 +123,13 @@ async function main() {
|
|
|
110
123
|
{ sessionId: envelope.session_id },
|
|
111
124
|
{ timeoutMs: DAEMON_TIMEOUT_MS }
|
|
112
125
|
);
|
|
126
|
+
if (isSessionEnded()) {
|
|
127
|
+
const notice = reauthNoticeOutput(agent);
|
|
128
|
+
if (notice) {
|
|
129
|
+
await emitOutput(notice);
|
|
130
|
+
return;
|
|
131
|
+
}
|
|
132
|
+
}
|
|
113
133
|
if (agent === "codex") {
|
|
114
134
|
const snapshot = await daemonRequest(
|
|
115
135
|
"status_snapshot",
|
package/dist/index.js
CHANGED
|
@@ -12,7 +12,7 @@ import {
|
|
|
12
12
|
checkAffectedDecisions,
|
|
13
13
|
daemonOrDirectGet,
|
|
14
14
|
formatDecisionsWarning
|
|
15
|
-
} from "./chunk-
|
|
15
|
+
} from "./chunk-VDLAAUWV.js";
|
|
16
16
|
import {
|
|
17
17
|
JOURNAL_DIR,
|
|
18
18
|
SESSIONS_DIR,
|
|
@@ -21,10 +21,10 @@ import {
|
|
|
21
21
|
listFlushing,
|
|
22
22
|
pendingJournalStats,
|
|
23
23
|
readMovesFromPath
|
|
24
|
-
} from "./chunk-
|
|
24
|
+
} from "./chunk-BOTKPLTI.js";
|
|
25
25
|
import {
|
|
26
26
|
fetchFeedbackCapability
|
|
27
|
-
} from "./chunk-
|
|
27
|
+
} from "./chunk-WELBNODJ.js";
|
|
28
28
|
import {
|
|
29
29
|
inspectWorkspaceId
|
|
30
30
|
} from "./chunk-IMAIBPUC.js";
|
|
@@ -56,7 +56,7 @@ import {
|
|
|
56
56
|
resolveAuthCredential,
|
|
57
57
|
setStoredToken,
|
|
58
58
|
withFileLock
|
|
59
|
-
} from "./chunk-
|
|
59
|
+
} from "./chunk-DWDEQRWN.js";
|
|
60
60
|
import {
|
|
61
61
|
daemonIsLive,
|
|
62
62
|
daemonRequest
|
|
@@ -64,7 +64,7 @@ import {
|
|
|
64
64
|
|
|
65
65
|
// src/index.ts
|
|
66
66
|
import { readFileSync as readFileSync12 } from "fs";
|
|
67
|
-
import { dirname as
|
|
67
|
+
import { dirname as dirname9, resolve as resolve6 } from "path";
|
|
68
68
|
import { fileURLToPath as fileURLToPath4 } from "url";
|
|
69
69
|
import { Command } from "commander";
|
|
70
70
|
import updateNotifier from "update-notifier";
|
|
@@ -4241,10 +4241,11 @@ function registerHooksCommands(program2) {
|
|
|
4241
4241
|
|
|
4242
4242
|
// src/commands/moves.ts
|
|
4243
4243
|
import { existsSync as existsSync8, mkdirSync as mkdirSync6, unlinkSync as unlinkSync4, writeFileSync as writeFileSync5 } from "fs";
|
|
4244
|
-
import { join as
|
|
4244
|
+
import { join as join8 } from "path";
|
|
4245
4245
|
|
|
4246
4246
|
// src/flusher.ts
|
|
4247
4247
|
import { createReadStream, renameSync as renameSync4, unlinkSync as unlinkSync3 } from "fs";
|
|
4248
|
+
import { dirname as dirname5, join as join7 } from "path";
|
|
4248
4249
|
import { createInterface } from "readline";
|
|
4249
4250
|
var BATCH_SIZE = 500;
|
|
4250
4251
|
var HTTP_TIMEOUT_MS = 1e4;
|
|
@@ -4368,11 +4369,23 @@ async function flushOnce() {
|
|
|
4368
4369
|
return { flushed: total };
|
|
4369
4370
|
}
|
|
4370
4371
|
var flushInFlight;
|
|
4372
|
+
var FLUSH_LOCK_PATH = join7(dirname5(JOURNAL_DIR), ".flush.lock");
|
|
4373
|
+
var FLUSH_LOCK_TIMEOUT_MS = 250;
|
|
4374
|
+
function isFlushLockContended(error) {
|
|
4375
|
+
return error instanceof Error && error.message.startsWith("timed out waiting for file lock");
|
|
4376
|
+
}
|
|
4371
4377
|
function flush() {
|
|
4372
4378
|
if (flushInFlight) {
|
|
4373
4379
|
return flushInFlight;
|
|
4374
4380
|
}
|
|
4375
|
-
const attempt =
|
|
4381
|
+
const attempt = withFileLock(FLUSH_LOCK_PATH, flushOnce, {
|
|
4382
|
+
timeoutMs: FLUSH_LOCK_TIMEOUT_MS
|
|
4383
|
+
}).catch((error) => {
|
|
4384
|
+
if (isFlushLockContended(error)) {
|
|
4385
|
+
return { flushed: 0, skipped: true };
|
|
4386
|
+
}
|
|
4387
|
+
throw error;
|
|
4388
|
+
}).finally(() => {
|
|
4376
4389
|
if (flushInFlight === attempt) {
|
|
4377
4390
|
flushInFlight = void 0;
|
|
4378
4391
|
}
|
|
@@ -4410,7 +4423,11 @@ var WORKSPACE_FILE = ".prim/workspace.json";
|
|
|
4410
4423
|
function registerMovesCommands(program2) {
|
|
4411
4424
|
const moves = program2.command("moves").description("Decision Event Pipeline \u2014 local journal");
|
|
4412
4425
|
moves.command("flush").description("Drain all local move journals to the server").action(async () => {
|
|
4413
|
-
const { flushed } = await flush();
|
|
4426
|
+
const { flushed, skipped } = await flush();
|
|
4427
|
+
if (skipped) {
|
|
4428
|
+
console.log("[prim] another flush is already draining; moves left journaled");
|
|
4429
|
+
return;
|
|
4430
|
+
}
|
|
4414
4431
|
console.log(`[prim] flushed ${String(flushed)} move${flushed === 1 ? "" : "s"}`);
|
|
4415
4432
|
});
|
|
4416
4433
|
moves.command("status").description("Show per-bucket pending stats").action(() => {
|
|
@@ -4469,18 +4486,18 @@ function registerMovesCommands(program2) {
|
|
|
4469
4486
|
}
|
|
4470
4487
|
});
|
|
4471
4488
|
moves.command("bind").description("Pin the current directory to an org via .prim/workspace.json").requiredOption("--orgId <orgId>", "Convex organization id").action((opts) => {
|
|
4472
|
-
const dir =
|
|
4489
|
+
const dir = join8(process.cwd(), ".prim");
|
|
4473
4490
|
if (!existsSync8(dir)) {
|
|
4474
4491
|
mkdirSync6(dir, { recursive: true, mode: DIR_MODE });
|
|
4475
4492
|
}
|
|
4476
|
-
const file =
|
|
4493
|
+
const file = join8(process.cwd(), WORKSPACE_FILE);
|
|
4477
4494
|
writeFileSync5(file, JSON.stringify({ orgId: opts.orgId, boundAt: Date.now() }, null, 2), {
|
|
4478
4495
|
mode: FILE_MODE
|
|
4479
4496
|
});
|
|
4480
4497
|
console.log(`[prim] bound ${process.cwd()} to org ${opts.orgId}`);
|
|
4481
4498
|
});
|
|
4482
4499
|
moves.command("drop").description("Remove the .prim/workspace.json binding from the cwd").action(() => {
|
|
4483
|
-
const file =
|
|
4500
|
+
const file = join8(process.cwd(), WORKSPACE_FILE);
|
|
4484
4501
|
if (!existsSync8(file)) {
|
|
4485
4502
|
console.log("[prim] no workspace binding in cwd");
|
|
4486
4503
|
return;
|
|
@@ -4581,7 +4598,7 @@ import {
|
|
|
4581
4598
|
unlinkSync as unlinkSync5,
|
|
4582
4599
|
writeFileSync as writeFileSync6
|
|
4583
4600
|
} from "fs";
|
|
4584
|
-
import { join as
|
|
4601
|
+
import { join as join9 } from "path";
|
|
4585
4602
|
var DIR_MODE2 = 448;
|
|
4586
4603
|
var FILE_MODE2 = 384;
|
|
4587
4604
|
function ensureDir() {
|
|
@@ -4590,7 +4607,7 @@ function ensureDir() {
|
|
|
4590
4607
|
}
|
|
4591
4608
|
}
|
|
4592
4609
|
function markerPath(sessionId) {
|
|
4593
|
-
return
|
|
4610
|
+
return join9(SESSIONS_DIR, `${sessionId}.json`);
|
|
4594
4611
|
}
|
|
4595
4612
|
function registerSessionCommands(program2) {
|
|
4596
4613
|
const session = program2.command("session").description("Decision Event Pipeline \u2014 session binding markers");
|
|
@@ -4618,7 +4635,7 @@ function registerSessionCommands(program2) {
|
|
|
4618
4635
|
for (const f of files) {
|
|
4619
4636
|
const sessionId = f.replace(/\.json$/, "");
|
|
4620
4637
|
try {
|
|
4621
|
-
const m = JSON.parse(readFileSync7(
|
|
4638
|
+
const m = JSON.parse(readFileSync7(join9(SESSIONS_DIR, f), "utf-8"));
|
|
4622
4639
|
console.log(`${sessionId} org=${m.orgId}`);
|
|
4623
4640
|
} catch {
|
|
4624
4641
|
}
|
|
@@ -4638,7 +4655,7 @@ function registerSessionCommands(program2) {
|
|
|
4638
4655
|
// src/commands/setup.ts
|
|
4639
4656
|
import { spawnSync as spawnSync2 } from "child_process";
|
|
4640
4657
|
import { existsSync as existsSync10, readFileSync as readFileSync8 } from "fs";
|
|
4641
|
-
import { join as
|
|
4658
|
+
import { join as join10 } from "path";
|
|
4642
4659
|
var EXIT_INCOMPLETE = 1;
|
|
4643
4660
|
var EXIT_USAGE3 = 2;
|
|
4644
4661
|
var SESSION_LABELS = {
|
|
@@ -4714,7 +4731,7 @@ function detectProjectConflicts(agent, run) {
|
|
|
4714
4731
|
}
|
|
4715
4732
|
try {
|
|
4716
4733
|
const root = gitToplevel();
|
|
4717
|
-
const preCommit = root &&
|
|
4734
|
+
const preCommit = root && join10(root, ".git", "hooks", "pre-commit");
|
|
4718
4735
|
if (preCommit && existsSync10(preCommit) && readFileSync8(preCommit, "utf-8").includes("prim-pre-commit")) {
|
|
4719
4736
|
conflicts.push(CONFLICT_HOOKS);
|
|
4720
4737
|
}
|
|
@@ -4865,31 +4882,31 @@ import {
|
|
|
4865
4882
|
writeFileSync as writeFileSync7
|
|
4866
4883
|
} from "fs";
|
|
4867
4884
|
import { homedir as homedir8 } from "os";
|
|
4868
|
-
import { dirname as
|
|
4885
|
+
import { dirname as dirname7, join as join12, resolve as resolve4 } from "path";
|
|
4869
4886
|
import { fileURLToPath as fileURLToPath2 } from "url";
|
|
4870
4887
|
import { createPatch } from "diff";
|
|
4871
4888
|
|
|
4872
4889
|
// src/commands/claude-plugin.ts
|
|
4873
4890
|
import { existsSync as existsSync11, mkdirSync as mkdirSync8, readFileSync as readFileSync9, readdirSync as readdirSync2, rmSync as rmSync3, rmdirSync } from "fs";
|
|
4874
4891
|
import { homedir as homedir7 } from "os";
|
|
4875
|
-
import { dirname as
|
|
4892
|
+
import { dirname as dirname6, join as join11, resolve as resolve3 } from "path";
|
|
4876
4893
|
import { fileURLToPath } from "url";
|
|
4877
|
-
var __dirname =
|
|
4894
|
+
var __dirname = dirname6(fileURLToPath(import.meta.url));
|
|
4878
4895
|
var PLUGIN_DESCRIPTION = "Primitive decision-graph guidance for the prim CLI \u2014 a model-invoked skill installed by prim skill install.";
|
|
4879
4896
|
function resolvePluginDir(cwd, scope) {
|
|
4880
4897
|
if (scope && scope !== "user" && scope !== "project") {
|
|
4881
4898
|
console.error(`Unknown --scope "${scope}" (expected user or project)`);
|
|
4882
4899
|
return null;
|
|
4883
4900
|
}
|
|
4884
|
-
const base = scope === "user" ?
|
|
4885
|
-
return
|
|
4901
|
+
const base = scope === "user" ? join11(homedir7(), ".claude") : join11(gitToplevel(cwd) ?? cwd, ".claude");
|
|
4902
|
+
return join11(base, "skills", "prim");
|
|
4886
4903
|
}
|
|
4887
4904
|
function packageVersion() {
|
|
4888
4905
|
let dir = __dirname;
|
|
4889
|
-
while (dir !==
|
|
4906
|
+
while (dir !== dirname6(dir)) {
|
|
4890
4907
|
const p = resolve3(dir, "package.json");
|
|
4891
4908
|
if (existsSync11(p)) return JSON.parse(readFileSync9(p, "utf-8")).version;
|
|
4892
|
-
dir =
|
|
4909
|
+
dir = dirname6(dir);
|
|
4893
4910
|
}
|
|
4894
4911
|
return "0.0.0";
|
|
4895
4912
|
}
|
|
@@ -4903,8 +4920,8 @@ function removeDirIfEmpty(dir) {
|
|
|
4903
4920
|
}
|
|
4904
4921
|
function pluginPaths(dir) {
|
|
4905
4922
|
return {
|
|
4906
|
-
manifestPath:
|
|
4907
|
-
skillPath:
|
|
4923
|
+
manifestPath: join11(dir, ".claude-plugin", "plugin.json"),
|
|
4924
|
+
skillPath: join11(dir, "SKILL.md")
|
|
4908
4925
|
};
|
|
4909
4926
|
}
|
|
4910
4927
|
function installClaudePlugin(cwd, opts) {
|
|
@@ -4923,7 +4940,7 @@ function installClaudePlugin(cwd, opts) {
|
|
|
4923
4940
|
console.log(`Would write plugin to ${dir} (.claude-plugin/plugin.json + SKILL.md)`);
|
|
4924
4941
|
return 0;
|
|
4925
4942
|
}
|
|
4926
|
-
mkdirSync8(
|
|
4943
|
+
mkdirSync8(join11(dir, ".claude-plugin"), { recursive: true });
|
|
4927
4944
|
atomicWrite3(manifestPath, manifest);
|
|
4928
4945
|
atomicWrite3(skillPath, skill);
|
|
4929
4946
|
console.log(`Installed prim skill plugin at ${dir}`);
|
|
@@ -4943,7 +4960,7 @@ function uninstallClaudePlugin(cwd, opts) {
|
|
|
4943
4960
|
}
|
|
4944
4961
|
rmSync3(manifestPath, { force: true });
|
|
4945
4962
|
rmSync3(skillPath, { force: true });
|
|
4946
|
-
removeDirIfEmpty(
|
|
4963
|
+
removeDirIfEmpty(join11(dir, ".claude-plugin"));
|
|
4947
4964
|
removeDirIfEmpty(dir);
|
|
4948
4965
|
console.log(`Removed prim skill plugin from ${dir}`);
|
|
4949
4966
|
return 0;
|
|
@@ -4966,7 +4983,7 @@ function statusClaudePlugin(cwd, opts) {
|
|
|
4966
4983
|
}
|
|
4967
4984
|
|
|
4968
4985
|
// src/commands/skill.ts
|
|
4969
|
-
var __dirname2 =
|
|
4986
|
+
var __dirname2 = dirname7(fileURLToPath2(import.meta.url));
|
|
4970
4987
|
var SKILL_BEGIN = "<!-- BEGIN PRIM SKILL v1 -->";
|
|
4971
4988
|
var SKILL_END = "<!-- END PRIM SKILL v1 -->";
|
|
4972
4989
|
var TARGET_CANDIDATES = [
|
|
@@ -4984,19 +5001,19 @@ var AGENT_TARGET = {
|
|
|
4984
5001
|
hermes: ".hermes.md"
|
|
4985
5002
|
};
|
|
4986
5003
|
function userTargetFor(agent) {
|
|
4987
|
-
if (agent === "claude") return
|
|
4988
|
-
if (agent === "codex") return
|
|
5004
|
+
if (agent === "claude") return join12(homedir8(), ".claude", "CLAUDE.md");
|
|
5005
|
+
if (agent === "codex") return join12(homedir8(), ".codex", "AGENTS.md");
|
|
4989
5006
|
if (agent === "hermes") {
|
|
4990
|
-
return
|
|
5007
|
+
return join12(process.env.HERMES_HOME ?? join12(homedir8(), ".hermes"), ".hermes.md");
|
|
4991
5008
|
}
|
|
4992
5009
|
return null;
|
|
4993
5010
|
}
|
|
4994
5011
|
function loadSkill() {
|
|
4995
5012
|
let dir = __dirname2;
|
|
4996
|
-
while (dir !==
|
|
5013
|
+
while (dir !== dirname7(dir)) {
|
|
4997
5014
|
const p = resolve4(dir, "SKILL.md");
|
|
4998
5015
|
if (existsSync12(p)) return readFileSync10(p, "utf-8");
|
|
4999
|
-
dir =
|
|
5016
|
+
dir = dirname7(dir);
|
|
5000
5017
|
}
|
|
5001
5018
|
throw new Error("SKILL.md not found in package");
|
|
5002
5019
|
}
|
|
@@ -5150,13 +5167,13 @@ function registerSkillCommands(program2) {
|
|
|
5150
5167
|
|
|
5151
5168
|
// src/commands/statusline.ts
|
|
5152
5169
|
import { readFileSync as readFileSync11 } from "fs";
|
|
5153
|
-
import { dirname as
|
|
5170
|
+
import { dirname as dirname8, resolve as resolve5 } from "path";
|
|
5154
5171
|
import { fileURLToPath as fileURLToPath3 } from "url";
|
|
5155
5172
|
var STATUSLINE_TIMEOUT_MS = 200;
|
|
5156
5173
|
var STATUSLINE_NAME_CAP = 3;
|
|
5157
5174
|
function readPackageVersion() {
|
|
5158
5175
|
try {
|
|
5159
|
-
const here =
|
|
5176
|
+
const here = dirname8(fileURLToPath3(import.meta.url));
|
|
5160
5177
|
const candidates = [
|
|
5161
5178
|
// The supervised runtime stages a tiny manifest beside the standalone
|
|
5162
5179
|
// statusline bundle, so it remains versioned without loading the package.
|
|
@@ -5197,6 +5214,9 @@ async function renderStatusline() {
|
|
|
5197
5214
|
return `primitive ${version} (daemon: down)`;
|
|
5198
5215
|
}
|
|
5199
5216
|
if (snapshot.healthy === false) {
|
|
5217
|
+
if (snapshot.needsReauth) {
|
|
5218
|
+
return `primitive ${version} (daemon: paused \xB7 run \`prim auth login\`)`;
|
|
5219
|
+
}
|
|
5200
5220
|
if (snapshot.ingestion?.healthy === false) {
|
|
5201
5221
|
const pending = snapshot.ingestion.pendingCount;
|
|
5202
5222
|
const qualifier = snapshot.ingestion.pendingSampled ? "at least " : "";
|
|
@@ -5350,7 +5370,7 @@ function registerWelcomeCommand(program2, deps = { getClient }) {
|
|
|
5350
5370
|
}
|
|
5351
5371
|
|
|
5352
5372
|
// src/index.ts
|
|
5353
|
-
var __dirname3 =
|
|
5373
|
+
var __dirname3 = dirname9(fileURLToPath4(import.meta.url));
|
|
5354
5374
|
var pkg = JSON.parse(readFileSync12(resolve6(__dirname3, "../package.json"), "utf-8"));
|
|
5355
5375
|
updateNotifier({ pkg }).notify();
|
|
5356
5376
|
var program = new Command();
|
package/dist/statusline-main.js
CHANGED
|
@@ -312,6 +312,9 @@ async function renderStatusline() {
|
|
|
312
312
|
return `primitive ${version} (daemon: down)`;
|
|
313
313
|
}
|
|
314
314
|
if (snapshot.healthy === false) {
|
|
315
|
+
if (snapshot.needsReauth) {
|
|
316
|
+
return `primitive ${version} (daemon: paused \xB7 run \`prim auth login\`)`;
|
|
317
|
+
}
|
|
315
318
|
if (snapshot.ingestion?.healthy === false) {
|
|
316
319
|
const pending = snapshot.ingestion.pendingCount;
|
|
317
320
|
const qualifier = snapshot.ingestion.pendingSampled ? "at least " : "";
|
package/package.json
CHANGED