sentinelayer-cli 0.44.0 → 0.46.0
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 +10 -5
- package/package.json +1 -1
- package/src/agents/jules/tools/aidenid-email.js +7 -9
- package/src/agents/jules/tools/frontend-analyze.js +15 -3
- package/src/agents/jules/tools/runtime-audit.js +51 -21
- package/src/agents/shared-tools/shell.js +9 -0
- package/src/ai/aidenid.js +5 -4
- package/src/ai/proxy.js +15 -14
- package/src/audit/orchestrator.js +8 -5
- package/src/auth/admission-scope.js +5 -3
- package/src/auth/credential-destinations.js +221 -0
- package/src/auth/gate.js +4 -4
- package/src/auth/http.js +22 -5
- package/src/auth/service.js +52 -56
- package/src/cli.js +24 -6
- package/src/commands/ai/provision-governance.js +7 -6
- package/src/commands/init.js +25 -16
- package/src/commands/scan.js +6 -4
- package/src/commands/session.js +33 -45
- package/src/commands/spec.js +2 -2
- package/src/commands/watch.js +4 -3
- package/src/config/service.js +37 -0
- package/src/daemon/ast-parser-layer.js +8 -3
- package/src/daemon/callgraph-overlay.js +7 -2
- package/src/daemon/pulse.js +2 -1
- package/src/daemon/watchdog.js +117 -68
- package/src/legacy-cli.js +119 -39
- package/src/mcp/bridge-inputs.js +223 -0
- package/src/mcp/cli-command-tools.js +157 -7
- package/src/mcp/registry.js +2 -1
- package/src/mcp/session-stdio-server.js +114 -35
- package/src/mcp/smoke.js +27 -11
- package/src/mcp/token-service.js +7 -9
- package/src/memory/retrieval.js +14 -5
- package/src/pocket/ring-owner.js +13 -8
- package/src/program-lookup.js +54 -0
- package/src/review/ai-review.js +2 -2
- package/src/review/dd-report-email-client.js +4 -5
- package/src/review/local-review.js +11 -5
- package/src/session/admission-access.js +13 -10
- package/src/session/admission-auth.js +95 -16
- package/src/session/admission.js +44 -9
- package/src/session/checkpoints.js +11 -10
- package/src/session/file-locks.js +19 -18
- package/src/session/invitations.js +26 -19
- package/src/session/live-source.js +13 -13
- package/src/session/route-classes.js +107 -0
- package/src/session/sync.js +70 -56
- package/src/session/tickets.js +21 -19
- package/src/session/title-sync.js +4 -3
- package/src/telemetry/sync.js +8 -9
package/src/session/tickets.js
CHANGED
|
@@ -19,6 +19,7 @@ import os from "node:os";
|
|
|
19
19
|
import path from "node:path";
|
|
20
20
|
import process from "node:process";
|
|
21
21
|
|
|
22
|
+
import { checkedTransport, credentialFor, isAuthenticated } from "../auth/credential-destinations.js";
|
|
22
23
|
import { requestJson, requestJsonMutation } from "../auth/http.js";
|
|
23
24
|
import { resolveActiveAuthSession } from "../auth/service.js";
|
|
24
25
|
|
|
@@ -70,8 +71,8 @@ async function writeState(filePath, state) {
|
|
|
70
71
|
|
|
71
72
|
async function auth(targetPath, resolveAuthSession) {
|
|
72
73
|
const session = await resolveAuthSession({ cwd: targetPath, env: process.env, autoRotate: false });
|
|
73
|
-
if (!session
|
|
74
|
-
return {
|
|
74
|
+
if (!isAuthenticated(session) || !session?.apiUrl) throw new Error("Not authenticated for this session.");
|
|
75
|
+
return { credential: await credentialFor(session), apiUrl: normalizeString(session.apiUrl).replace(/\/+$/, "") };
|
|
75
76
|
}
|
|
76
77
|
|
|
77
78
|
function ticketsUrl(apiUrl, sessionId, suffix = "") {
|
|
@@ -155,7 +156,7 @@ export async function listTickets(
|
|
|
155
156
|
maxPages = SNAPSHOT_MAX_PAGES,
|
|
156
157
|
} = {}
|
|
157
158
|
) {
|
|
158
|
-
const {
|
|
159
|
+
const { credential, apiUrl } = await auth(targetPath, resolveAuthSession);
|
|
159
160
|
const items = [];
|
|
160
161
|
let cursor = null;
|
|
161
162
|
let afterId = null;
|
|
@@ -164,9 +165,9 @@ export async function listTickets(
|
|
|
164
165
|
const query = new URLSearchParams({ limit: String(pageSize) });
|
|
165
166
|
if (afterId) query.set("afterId", afterId);
|
|
166
167
|
const body = validatedSnapshotPage(
|
|
167
|
-
await requestRead(`${ticketsUrl(apiUrl, sessionId, "/snapshot")}?${query}`, {
|
|
168
|
+
await checkedTransport(requestRead)(`${ticketsUrl(apiUrl, sessionId, "/snapshot")}?${query}`, {
|
|
168
169
|
method: "GET",
|
|
169
|
-
|
|
170
|
+
credential,
|
|
170
171
|
}),
|
|
171
172
|
{ first: page === 0, afterId },
|
|
172
173
|
);
|
|
@@ -192,19 +193,20 @@ export async function ticketEvents(
|
|
|
192
193
|
sessionId,
|
|
193
194
|
{ after = 0, targetPath = process.cwd(), resolveAuthSession = resolveActiveAuthSession, requestRead = requestJson } = {}
|
|
194
195
|
) {
|
|
195
|
-
const {
|
|
196
|
-
return requestRead(`${ticketsUrl(apiUrl, sessionId, "/events")}?after=${encodeURIComponent(String(after))}`, {
|
|
196
|
+
const { credential, apiUrl } = await auth(targetPath, resolveAuthSession);
|
|
197
|
+
return checkedTransport(requestRead)(`${ticketsUrl(apiUrl, sessionId, "/events")}?after=${encodeURIComponent(String(after))}`, {
|
|
197
198
|
method: "GET",
|
|
198
|
-
|
|
199
|
+
credential,
|
|
199
200
|
});
|
|
200
201
|
}
|
|
201
202
|
|
|
202
|
-
async function mutate(url, {
|
|
203
|
-
return requestMutation(url, {
|
|
203
|
+
async function mutate(url, { credential, key, operation, body, requestMutation }) {
|
|
204
|
+
return checkedTransport(requestMutation)(url, {
|
|
204
205
|
method: "POST",
|
|
205
206
|
operationName: `session.ticket_${operation}`,
|
|
206
207
|
idempotencyKey: key,
|
|
207
|
-
|
|
208
|
+
credential,
|
|
209
|
+
headers: { "Idempotency-Key": key },
|
|
208
210
|
body,
|
|
209
211
|
});
|
|
210
212
|
}
|
|
@@ -222,10 +224,10 @@ export async function claimTicket(
|
|
|
222
224
|
} = {}
|
|
223
225
|
) {
|
|
224
226
|
const statePath = ticketLeasePath(sessionId, ticketId, identity, { homeDir });
|
|
225
|
-
const {
|
|
227
|
+
const { credential, apiUrl } = await auth(targetPath, resolveAuthSession);
|
|
226
228
|
const { key } = await operationKey(statePath, "claim", String(expectedVersion ?? "any"));
|
|
227
229
|
const response = await mutate(ticketsUrl(apiUrl, sessionId, `/${encodeURIComponent(ticketId)}/claim`), {
|
|
228
|
-
|
|
230
|
+
credential, key, operation: "claim", requestMutation,
|
|
229
231
|
body: expectedVersion == null ? {} : { expectedVersion: Number(expectedVersion) },
|
|
230
232
|
});
|
|
231
233
|
// The lease is recorded here only if the server says it is held by the identity that
|
|
@@ -258,10 +260,10 @@ export async function renewTicketLease(
|
|
|
258
260
|
) {
|
|
259
261
|
const statePath = ticketLeasePath(sessionId, ticketId, identity, { homeDir });
|
|
260
262
|
const held = await heldLease(statePath);
|
|
261
|
-
const {
|
|
263
|
+
const { credential, apiUrl } = await auth(targetPath, resolveAuthSession);
|
|
262
264
|
const { key } = await operationKey(statePath, "renew", `${held.leaseId}:${held.fence}:${held.ticketVersion}`);
|
|
263
265
|
const response = await mutate(ticketsUrl(apiUrl, sessionId, `/${encodeURIComponent(ticketId)}/lease/renew`), {
|
|
264
|
-
|
|
266
|
+
credential, key, operation: "renew", requestMutation, body: { leaseId: held.leaseId, fence: held.fence },
|
|
265
267
|
});
|
|
266
268
|
await writeState(statePath, leaseState(response));
|
|
267
269
|
return response;
|
|
@@ -274,10 +276,10 @@ export async function releaseTicketLease(
|
|
|
274
276
|
) {
|
|
275
277
|
const statePath = ticketLeasePath(sessionId, ticketId, identity, { homeDir });
|
|
276
278
|
const held = await heldLease(statePath);
|
|
277
|
-
const {
|
|
279
|
+
const { credential, apiUrl } = await auth(targetPath, resolveAuthSession);
|
|
278
280
|
const { key } = await operationKey(statePath, "release", `${held.leaseId}:${held.fence}:${held.ticketVersion}`);
|
|
279
281
|
const response = await mutate(ticketsUrl(apiUrl, sessionId, `/${encodeURIComponent(ticketId)}/lease/release`), {
|
|
280
|
-
|
|
282
|
+
credential, key, operation: "release", requestMutation,
|
|
281
283
|
body: { leaseId: held.leaseId, fence: held.fence, expectedVersion: held.ticketVersion, ...(reason ? { reason } : {}) },
|
|
282
284
|
});
|
|
283
285
|
await fsp.rm(statePath, { force: true });
|
|
@@ -299,10 +301,10 @@ export async function submitTicket(
|
|
|
299
301
|
) {
|
|
300
302
|
const statePath = ticketLeasePath(sessionId, ticketId, identity, { homeDir });
|
|
301
303
|
const held = await heldLease(statePath);
|
|
302
|
-
const {
|
|
304
|
+
const { credential, apiUrl } = await auth(targetPath, resolveAuthSession);
|
|
303
305
|
const { key } = await operationKey(statePath, "submit", `${held.leaseId}:${held.fence}:${held.ticketVersion}`);
|
|
304
306
|
const response = await mutate(ticketsUrl(apiUrl, sessionId, `/${encodeURIComponent(ticketId)}/submit`), {
|
|
305
|
-
|
|
307
|
+
credential, key, operation: "submit", requestMutation,
|
|
306
308
|
body: {
|
|
307
309
|
leaseId: held.leaseId,
|
|
308
310
|
fence: held.fence,
|
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import process from "node:process";
|
|
2
2
|
|
|
3
|
+
import { checkedTransport, credentialFor, isAuthenticated } from "../auth/credential-destinations.js";
|
|
3
4
|
import { SentinelayerApiError, requestJsonMutation } from "../auth/http.js";
|
|
4
5
|
import { resolveActiveAuthSession } from "../auth/service.js";
|
|
5
6
|
import { recordSessionRemoteTitleSync } from "./store.js";
|
|
@@ -66,7 +67,7 @@ export async function pushSessionTitleToApi(
|
|
|
66
67
|
env,
|
|
67
68
|
autoRotate,
|
|
68
69
|
});
|
|
69
|
-
if (!session
|
|
70
|
+
if (!isAuthenticated(session) || !session?.apiUrl) {
|
|
70
71
|
await recordRemoteTitleSync(normalizedSessionId, {
|
|
71
72
|
targetPath,
|
|
72
73
|
title: normalizedTitle,
|
|
@@ -76,12 +77,12 @@ export async function pushSessionTitleToApi(
|
|
|
76
77
|
return { synced: false, reason: "not_authenticated" };
|
|
77
78
|
}
|
|
78
79
|
const apiUrl = String(session.apiUrl).replace(/\/+$/, "");
|
|
79
|
-
const result = await requestMutation(
|
|
80
|
+
const result = await checkedTransport(requestMutation)(
|
|
80
81
|
`${apiUrl}/api/v1/sessions/${encodeURIComponent(normalizedSessionId)}/title`,
|
|
81
82
|
{
|
|
82
83
|
method: "POST",
|
|
83
84
|
operationName: "session.set_title",
|
|
84
|
-
|
|
85
|
+
credential: await credentialFor(session, { env }),
|
|
85
86
|
body: { title: normalizedTitle },
|
|
86
87
|
timeoutMs,
|
|
87
88
|
maxRetries,
|
package/src/telemetry/sync.js
CHANGED
|
@@ -3,6 +3,7 @@ import { randomUUID } from "node:crypto";
|
|
|
3
3
|
import { readFileSync } from "node:fs";
|
|
4
4
|
import { fileURLToPath } from "node:url";
|
|
5
5
|
import { dirname, resolve } from "node:path";
|
|
6
|
+
import { credentialedRequest, credentialFor, isAuthenticated } from "../auth/credential-destinations.js";
|
|
6
7
|
import { resolveActiveAuthSession } from "../auth/service.js";
|
|
7
8
|
|
|
8
9
|
// Read CLI version from package.json at module load
|
|
@@ -71,7 +72,7 @@ export async function syncRunToDashboard(runData) {
|
|
|
71
72
|
return { synced: false, reason: "no_session" };
|
|
72
73
|
}
|
|
73
74
|
|
|
74
|
-
if (!session
|
|
75
|
+
if (!isAuthenticated(session)) {
|
|
75
76
|
return { synced: false, reason: "not_authenticated" };
|
|
76
77
|
}
|
|
77
78
|
|
|
@@ -121,14 +122,12 @@ export async function syncRunToDashboard(runData) {
|
|
|
121
122
|
},
|
|
122
123
|
};
|
|
123
124
|
|
|
124
|
-
const response = await
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
body: JSON.stringify(payload),
|
|
131
|
-
}, SYNC_TIMEOUT_MS);
|
|
125
|
+
const response = await credentialedRequest(
|
|
126
|
+
await credentialFor(session),
|
|
127
|
+
apiUrl + "/api/v1/telemetry",
|
|
128
|
+
{ method: "POST", headers: { "Content-Type": "application/json" }, body: JSON.stringify(payload) },
|
|
129
|
+
{ fetchImpl: (target, init) => fetchWithTimeout(target, init, SYNC_TIMEOUT_MS) },
|
|
130
|
+
);
|
|
132
131
|
|
|
133
132
|
if (!response.ok) {
|
|
134
133
|
consecutiveFailures++;
|