sentinelayer-cli 0.6.2 → 0.8.1
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 +1009 -996
- package/bin/create-sentinelayer.js +5 -5
- package/bin/sentinelayer-cli.js +4 -4
- package/bin/sl.js +5 -5
- package/package.json +64 -63
- package/src/agents/ai-governance/index.js +12 -0
- package/src/agents/ai-governance/tools/base.js +171 -0
- package/src/agents/ai-governance/tools/eval-regression.js +47 -0
- package/src/agents/ai-governance/tools/hitl-audit.js +81 -0
- package/src/agents/ai-governance/tools/index.js +52 -0
- package/src/agents/ai-governance/tools/prompt-drift.js +42 -0
- package/src/agents/ai-governance/tools/provenance-check.js +69 -0
- package/src/agents/backend/index.js +12 -0
- package/src/agents/backend/tools/base.js +189 -0
- package/src/agents/backend/tools/circuit-breaker-check.js +123 -0
- package/src/agents/backend/tools/idempotency-audit.js +105 -0
- package/src/agents/backend/tools/index.js +87 -0
- package/src/agents/backend/tools/retry-audit.js +132 -0
- package/src/agents/backend/tools/timeout-audit.js +144 -0
- package/src/agents/code-quality/index.js +12 -0
- package/src/agents/code-quality/tools/base.js +159 -0
- package/src/agents/code-quality/tools/complexity-measure.js +197 -0
- package/src/agents/code-quality/tools/coupling-analysis.js +81 -0
- package/src/agents/code-quality/tools/cycle-detect.js +49 -0
- package/src/agents/code-quality/tools/dep-graph.js +196 -0
- package/src/agents/code-quality/tools/index.js +89 -0
- package/src/agents/data-layer/index.js +12 -0
- package/src/agents/data-layer/tools/base.js +181 -0
- package/src/agents/data-layer/tools/index-audit.js +165 -0
- package/src/agents/data-layer/tools/index.js +83 -0
- package/src/agents/data-layer/tools/migration-scan.js +135 -0
- package/src/agents/data-layer/tools/query-explain.js +120 -0
- package/src/agents/data-layer/tools/tenancy-scan.js +166 -0
- package/src/agents/documentation/index.js +12 -0
- package/src/agents/documentation/tools/api-diff.js +91 -0
- package/src/agents/documentation/tools/base.js +151 -0
- package/src/agents/documentation/tools/dead-link-check.js +58 -0
- package/src/agents/documentation/tools/docstring-coverage.js +78 -0
- package/src/agents/documentation/tools/index.js +52 -0
- package/src/agents/documentation/tools/readme-freshness.js +61 -0
- package/src/agents/envelope/fix-cycle.js +45 -0
- package/src/agents/envelope/index.js +31 -0
- package/src/agents/envelope/loop.js +150 -0
- package/src/agents/envelope/pulse.js +18 -0
- package/src/agents/envelope/stream.js +40 -0
- package/src/agents/infrastructure/index.js +12 -0
- package/src/agents/infrastructure/tools/base.js +171 -0
- package/src/agents/infrastructure/tools/checkov-run.js +32 -0
- package/src/agents/infrastructure/tools/drift-detect.js +59 -0
- package/src/agents/infrastructure/tools/iam-least-priv-check.js +78 -0
- package/src/agents/infrastructure/tools/index.js +52 -0
- package/src/agents/infrastructure/tools/tflint-run.js +31 -0
- package/src/agents/jules/config/definition.js +160 -160
- package/src/agents/jules/config/system-prompt.js +182 -182
- package/src/agents/jules/error-intake.js +51 -51
- package/src/agents/jules/fix-cycle.js +17 -17
- package/src/agents/jules/loop.js +460 -450
- package/src/agents/jules/pulse.js +10 -10
- package/src/agents/jules/stream.js +187 -186
- package/src/agents/jules/swarm/file-scanner.js +74 -74
- package/src/agents/jules/swarm/index.js +11 -11
- package/src/agents/jules/swarm/orchestrator.js +362 -362
- package/src/agents/jules/swarm/pattern-hunter.js +123 -123
- package/src/agents/jules/swarm/sub-agent.js +315 -309
- package/src/agents/jules/tools/aidenid-email.js +189 -189
- package/src/agents/jules/tools/auth-audit.js +1708 -1691
- package/src/agents/jules/tools/dispatch.js +340 -335
- package/src/agents/jules/tools/file-edit.js +2 -2
- package/src/agents/jules/tools/file-read.js +2 -2
- package/src/agents/jules/tools/frontend-analyze.js +570 -570
- package/src/agents/jules/tools/glob.js +2 -2
- package/src/agents/jules/tools/grep.js +2 -2
- package/src/agents/jules/tools/index.js +29 -29
- package/src/agents/jules/tools/path-guards.js +2 -2
- package/src/agents/jules/tools/runtime-audit.js +507 -507
- package/src/agents/jules/tools/shell.js +2 -2
- package/src/agents/jules/tools/url-policy.js +100 -100
- package/src/agents/mode.js +113 -0
- package/src/agents/observability/index.js +12 -0
- package/src/agents/observability/tools/alert-audit.js +39 -0
- package/src/agents/observability/tools/base.js +181 -0
- package/src/agents/observability/tools/dashboard-gap.js +42 -0
- package/src/agents/observability/tools/index.js +54 -0
- package/src/agents/observability/tools/log-schema-check.js +74 -0
- package/src/agents/observability/tools/span-coverage.js +74 -0
- package/src/agents/persona-visuals.js +102 -61
- package/src/agents/release/index.js +12 -0
- package/src/agents/release/tools/base.js +181 -0
- package/src/agents/release/tools/changelog-diff.js +86 -0
- package/src/agents/release/tools/feature-flag-audit.js +126 -0
- package/src/agents/release/tools/index.js +61 -0
- package/src/agents/release/tools/rollback-verify.js +129 -0
- package/src/agents/release/tools/semver-check.js +109 -0
- package/src/agents/reliability/index.js +12 -0
- package/src/agents/reliability/tools/backpressure-check.js +129 -0
- package/src/agents/reliability/tools/base.js +181 -0
- package/src/agents/reliability/tools/chaos-probe.js +109 -0
- package/src/agents/reliability/tools/graceful-degradation-check.js +114 -0
- package/src/agents/reliability/tools/health-check-audit.js +111 -0
- package/src/agents/reliability/tools/index.js +87 -0
- package/src/agents/run-persona.js +109 -0
- package/src/agents/security/index.js +12 -0
- package/src/agents/security/tools/authz-audit.js +134 -0
- package/src/agents/security/tools/base.js +190 -0
- package/src/agents/security/tools/crypto-review.js +175 -0
- package/src/agents/security/tools/index.js +97 -0
- package/src/agents/security/tools/sast-scan.js +175 -0
- package/src/agents/security/tools/secrets-scan.js +216 -0
- package/src/agents/shared-tools/dispatch-core.js +320 -315
- package/src/agents/shared-tools/file-edit.js +180 -180
- package/src/agents/shared-tools/file-read.js +100 -100
- package/src/agents/shared-tools/glob.js +168 -168
- package/src/agents/shared-tools/grep.js +228 -228
- package/src/agents/shared-tools/index.js +46 -46
- package/src/agents/shared-tools/path-guards.js +161 -161
- package/src/agents/shared-tools/shell.js +383 -383
- package/src/agents/supply-chain/index.js +12 -0
- package/src/agents/supply-chain/tools/attestation-check.js +42 -0
- package/src/agents/supply-chain/tools/base.js +151 -0
- package/src/agents/supply-chain/tools/index.js +52 -0
- package/src/agents/supply-chain/tools/lockfile-integrity.js +73 -0
- package/src/agents/supply-chain/tools/package-verify.js +56 -0
- package/src/agents/supply-chain/tools/sbom-diff.js +34 -0
- package/src/agents/testing/index.js +12 -0
- package/src/agents/testing/tools/base.js +202 -0
- package/src/agents/testing/tools/coverage-gap.js +144 -0
- package/src/agents/testing/tools/flake-detect.js +125 -0
- package/src/agents/testing/tools/index.js +85 -0
- package/src/agents/testing/tools/mutation-test.js +143 -0
- package/src/agents/testing/tools/snapshot-diff.js +103 -0
- package/src/ai/aidenid.js +1021 -1009
- package/src/ai/client.js +553 -553
- package/src/ai/domain-target-store.js +268 -268
- package/src/ai/identity-store.js +270 -270
- package/src/ai/proxy.js +137 -137
- package/src/ai/site-store.js +145 -145
- package/src/audit/agents/architecture.js +180 -180
- package/src/audit/agents/compliance.js +179 -179
- package/src/audit/agents/documentation.js +165 -165
- package/src/audit/agents/performance.js +145 -145
- package/src/audit/agents/security.js +215 -215
- package/src/audit/agents/testing.js +172 -172
- package/src/audit/orchestrator.js +557 -557
- package/src/audit/package.js +204 -204
- package/src/audit/registry.js +284 -284
- package/src/audit/replay.js +103 -103
- package/src/auth/gate.js +428 -371
- package/src/auth/http.js +681 -611
- package/src/auth/service.js +1106 -1106
- package/src/auth/session-store.js +813 -813
- package/src/cli.js +257 -252
- package/src/commands/ai/identity-lifecycle.js +1338 -1338
- package/src/commands/ai/provision-governance.js +1272 -1272
- package/src/commands/ai/shared.js +147 -147
- package/src/commands/ai.js +11 -11
- package/src/commands/apply.js +12 -12
- package/src/commands/audit.js +1171 -1166
- package/src/commands/auth.js +419 -419
- package/src/commands/chat.js +184 -191
- package/src/commands/config.js +184 -184
- package/src/commands/cost.js +311 -311
- package/src/commands/daemon/core.js +850 -850
- package/src/commands/daemon/extended.js +1048 -1048
- package/src/commands/daemon/shared.js +213 -213
- package/src/commands/daemon.js +11 -11
- package/src/commands/guide.js +174 -174
- package/src/commands/ingest.js +58 -58
- package/src/commands/init.js +55 -55
- package/src/commands/legacy-args.js +20 -10
- package/src/commands/mcp.js +461 -461
- package/src/commands/omargate.js +63 -29
- package/src/commands/persona.js +65 -20
- package/src/commands/plugin.js +260 -260
- package/src/commands/policy.js +132 -132
- package/src/commands/prompt.js +238 -238
- package/src/commands/review.js +704 -704
- package/src/commands/scan.js +865 -872
- package/src/commands/session.js +1238 -0
- package/src/commands/spec.js +771 -716
- package/src/commands/swarm.js +651 -651
- package/src/commands/telemetry.js +202 -202
- package/src/commands/watch.js +511 -511
- package/src/config/agent-dictionary.js +182 -182
- package/src/config/io.js +56 -56
- package/src/config/paths.js +18 -18
- package/src/config/schema.js +55 -55
- package/src/config/service.js +184 -184
- package/src/coord/events-log.js +141 -0
- package/src/coord/handshake.js +719 -0
- package/src/coord/index.js +35 -0
- package/src/coord/paths.js +84 -0
- package/src/coord/priority.js +62 -0
- package/src/coord/tarjan.js +157 -0
- package/src/cost/budget.js +235 -235
- package/src/cost/history.js +188 -188
- package/src/cost/tokenizer.js +160 -0
- package/src/cost/tracker.js +232 -171
- package/src/daemon/artifact-lineage.js +896 -534
- package/src/daemon/assignment-ledger.js +1083 -770
- package/src/daemon/ast-drift.js +496 -0
- package/src/daemon/ast-parser-layer.js +258 -258
- package/src/daemon/budget-governor.js +633 -633
- package/src/daemon/callgraph-overlay.js +646 -646
- package/src/daemon/error-worker.js +1209 -626
- package/src/daemon/fix-cycle.js +384 -377
- package/src/daemon/hybrid-mapper.js +929 -929
- package/src/daemon/ingest-refresh.js +79 -11
- package/src/daemon/jira-lifecycle.js +767 -632
- package/src/daemon/operator-control.js +657 -657
- package/src/daemon/pulse.js +327 -327
- package/src/daemon/reliability-lane.js +471 -471
- package/src/daemon/scope-engine.js +1068 -0
- package/src/daemon/watchdog.js +971 -971
- package/src/events/schema.js +190 -0
- package/src/guide/generator.js +316 -316
- package/src/ingest/engine.js +933 -918
- package/src/ingest/ownership.js +380 -0
- package/src/interactive/index.js +97 -97
- package/src/legacy-cli.js +3228 -2994
- package/src/mcp/registry.js +695 -695
- package/src/memory/blackboard.js +301 -301
- package/src/memory/retrieval.js +581 -581
- package/src/orchestrator/kai-chen.js +126 -0
- package/src/plugin/manifest.js +553 -553
- package/src/policy/packs.js +144 -144
- package/src/prompt/generator.js +136 -118
- package/src/review/ai-review.js +672 -679
- package/src/review/compliance-pack.js +389 -0
- package/src/review/investor-dd-config.js +54 -0
- package/src/review/investor-dd-file-loop.js +303 -0
- package/src/review/investor-dd-file-router.js +406 -0
- package/src/review/investor-dd-html-report.js +233 -0
- package/src/review/investor-dd-notification.js +120 -0
- package/src/review/investor-dd-orchestrator.js +405 -0
- package/src/review/investor-dd-persona-runner.js +275 -0
- package/src/review/live-validator.js +253 -0
- package/src/review/local-review.js +1351 -1305
- package/src/review/omargate-interactive.js +68 -68
- package/src/review/omargate-orchestrator.js +492 -300
- package/src/review/persona-prompts.js +484 -296
- package/src/review/reconciliation-rules.js +329 -0
- package/src/review/replay.js +235 -235
- package/src/review/report.js +664 -664
- package/src/review/reproducibility-chain.js +136 -0
- package/src/review/scan-modes.js +147 -42
- package/src/review/spec-binding.js +487 -487
- package/src/scaffold/generator.js +67 -67
- package/src/scaffold/templates.js +150 -150
- package/src/scan/generator.js +418 -418
- package/src/scan/gh-secrets.js +107 -107
- package/src/session/agent-registry.js +359 -0
- package/src/session/analytics.js +479 -0
- package/src/session/daemon.js +1396 -0
- package/src/session/file-locks.js +666 -0
- package/src/session/paths.js +37 -0
- package/src/session/recap.js +567 -0
- package/src/session/redact.js +82 -0
- package/src/session/runtime-bridge.js +762 -0
- package/src/session/scoring.js +406 -0
- package/src/session/setup-guides.js +304 -0
- package/src/session/store.js +704 -0
- package/src/session/stream.js +333 -0
- package/src/session/sync.js +753 -0
- package/src/session/tasks.js +1054 -0
- package/src/session/templates.js +188 -0
- package/src/spec/generator.js +619 -519
- package/src/spec/regenerate.js +237 -237
- package/src/spec/templates.js +91 -91
- package/src/swarm/dashboard.js +247 -247
- package/src/swarm/factory.js +363 -363
- package/src/swarm/pentest.js +934 -934
- package/src/swarm/registry.js +419 -419
- package/src/swarm/report.js +158 -158
- package/src/swarm/runtime.js +569 -576
- package/src/swarm/scenario-dsl.js +272 -272
- package/src/telemetry/ledger.js +302 -302
- package/src/telemetry/session-tracker.js +234 -234
- package/src/telemetry/sync.js +203 -203
- package/src/ui/command-hints.js +13 -13
- package/src/ui/markdown.js +220 -220
package/src/ai/proxy.js
CHANGED
|
@@ -1,137 +1,137 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* SentinelLayer LLM proxy provider.
|
|
3
|
-
*
|
|
4
|
-
* Routes LLM calls through POST /api/v1/proxy/llm using the stored
|
|
5
|
-
* sentinelayer_token. Users never need their own OpenAI/Anthropic key.
|
|
6
|
-
*
|
|
7
|
-
* Request: { model, system_prompt, user_content, max_tokens, temperature }
|
|
8
|
-
* Response: { content, usage: { model, provider, tokens_in, tokens_out, cost_usd, latency_ms } }
|
|
9
|
-
*/
|
|
10
|
-
|
|
11
|
-
import { resolveActiveAuthSession } from "../auth/service.js";
|
|
12
|
-
import { authLoginHint } from "../ui/command-hints.js";
|
|
13
|
-
|
|
14
|
-
const DEFAULT_PROXY_MODEL = "gpt-5.3-codex";
|
|
15
|
-
const PROXY_TIMEOUT_MS = 120_000;
|
|
16
|
-
const PROXY_MAX_RETRIES = 2;
|
|
17
|
-
const PROXY_RETRY_STATUSES = new Set([429, 502, 503, 504]);
|
|
18
|
-
|
|
19
|
-
/**
|
|
20
|
-
* Invoke LLM via SentinelLayer proxy.
|
|
21
|
-
*
|
|
22
|
-
* @param {object} options
|
|
23
|
-
* @param {string} options.prompt - The user content / prompt text
|
|
24
|
-
* @param {string} [options.systemPrompt] - System prompt
|
|
25
|
-
* @param {string} [options.model] - Model ID (default: gpt-5.3-codex)
|
|
26
|
-
* @param {number} [options.maxTokens] - Max output tokens (default: 4096)
|
|
27
|
-
* @param {number} [options.temperature] - Temperature (default: 0.1)
|
|
28
|
-
* @param {string} [options.apiUrl] - Override API URL
|
|
29
|
-
* @param {string} [options.token] - Override Bearer token
|
|
30
|
-
* @returns {Promise<{ text: string, usage: { inputTokens: number, outputTokens: number, costUsd: number, model: string, provider: string, latencyMs: number } }>}
|
|
31
|
-
*/
|
|
32
|
-
export async function invokeViaProxy({
|
|
33
|
-
prompt,
|
|
34
|
-
systemPrompt = "",
|
|
35
|
-
model = DEFAULT_PROXY_MODEL,
|
|
36
|
-
maxTokens = 4096,
|
|
37
|
-
temperature = 0.1,
|
|
38
|
-
apiUrl = "",
|
|
39
|
-
token = "",
|
|
40
|
-
} = {}) {
|
|
41
|
-
// Resolve credentials from session if not provided
|
|
42
|
-
let resolvedApiUrl = String(apiUrl || "").trim();
|
|
43
|
-
let resolvedToken = String(token || "").trim();
|
|
44
|
-
|
|
45
|
-
if (!resolvedApiUrl || !resolvedToken) {
|
|
46
|
-
const session = await resolveActiveAuthSession({
|
|
47
|
-
cwd: process.cwd(),
|
|
48
|
-
env: process.env,
|
|
49
|
-
autoRotate: false,
|
|
50
|
-
});
|
|
51
|
-
if (!session || !session.token) {
|
|
52
|
-
throw new Error(
|
|
53
|
-
`SentinelLayer LLM proxy requires authentication. Run '${authLoginHint()}' first.`
|
|
54
|
-
);
|
|
55
|
-
}
|
|
56
|
-
if (!resolvedApiUrl) resolvedApiUrl = String(session.apiUrl || "https://api.sentinelayer.com").trim();
|
|
57
|
-
if (!resolvedToken) resolvedToken = String(session.token).trim();
|
|
58
|
-
}
|
|
59
|
-
|
|
60
|
-
const url = `${resolvedApiUrl.replace(/\/+$/, "")}/api/v1/proxy/llm`;
|
|
61
|
-
|
|
62
|
-
const body = JSON.stringify({
|
|
63
|
-
model,
|
|
64
|
-
system_prompt: systemPrompt || "You are a code reviewer.",
|
|
65
|
-
user_content: String(prompt || ""),
|
|
66
|
-
max_tokens: maxTokens,
|
|
67
|
-
temperature,
|
|
68
|
-
});
|
|
69
|
-
|
|
70
|
-
let response = null;
|
|
71
|
-
let lastError = null;
|
|
72
|
-
|
|
73
|
-
for (let attempt = 0; attempt <= PROXY_MAX_RETRIES; attempt++) {
|
|
74
|
-
try {
|
|
75
|
-
const controller = new AbortController();
|
|
76
|
-
const timeoutHandle = setTimeout(() => controller.abort(), PROXY_TIMEOUT_MS);
|
|
77
|
-
try {
|
|
78
|
-
response = await fetch(url, {
|
|
79
|
-
method: "POST",
|
|
80
|
-
headers: {
|
|
81
|
-
"Content-Type": "application/json",
|
|
82
|
-
Authorization: `Bearer ${resolvedToken}`,
|
|
83
|
-
Accept: "application/json",
|
|
84
|
-
},
|
|
85
|
-
body,
|
|
86
|
-
signal: controller.signal,
|
|
87
|
-
});
|
|
88
|
-
} finally {
|
|
89
|
-
clearTimeout(timeoutHandle);
|
|
90
|
-
}
|
|
91
|
-
|
|
92
|
-
if (PROXY_RETRY_STATUSES.has(response.status) && attempt < PROXY_MAX_RETRIES) {
|
|
93
|
-
const retryAfter = response.headers.get("Retry-After");
|
|
94
|
-
const delay = retryAfter && !isNaN(retryAfter) ? Math.min(Number(retryAfter), 5) * 1000 : 500 * (attempt + 1);
|
|
95
|
-
await new Promise((r) => setTimeout(r, delay));
|
|
96
|
-
continue;
|
|
97
|
-
}
|
|
98
|
-
|
|
99
|
-
break;
|
|
100
|
-
} catch (err) {
|
|
101
|
-
lastError = err;
|
|
102
|
-
if (attempt >= PROXY_MAX_RETRIES) break;
|
|
103
|
-
await new Promise((r) => setTimeout(r, 500 * (attempt + 1)));
|
|
104
|
-
}
|
|
105
|
-
}
|
|
106
|
-
|
|
107
|
-
if (!response) {
|
|
108
|
-
throw new Error(`SentinelLayer LLM proxy request failed: ${lastError?.message || "no response"}`);
|
|
109
|
-
}
|
|
110
|
-
|
|
111
|
-
if (!response.ok) {
|
|
112
|
-
let detail = "";
|
|
113
|
-
try {
|
|
114
|
-
const errBody = await response.json();
|
|
115
|
-
detail = errBody?.error?.message || errBody?.detail || JSON.stringify(errBody).slice(0, 200);
|
|
116
|
-
} catch {
|
|
117
|
-
detail = await response.text().catch(() => "");
|
|
118
|
-
}
|
|
119
|
-
throw new Error(`SentinelLayer LLM proxy error (${response.status}): ${detail}`);
|
|
120
|
-
}
|
|
121
|
-
|
|
122
|
-
const result = await response.json();
|
|
123
|
-
|
|
124
|
-
return {
|
|
125
|
-
text: String(result.content || ""),
|
|
126
|
-
usage: {
|
|
127
|
-
inputTokens: result.usage?.tokens_in || 0,
|
|
128
|
-
outputTokens: result.usage?.tokens_out || 0,
|
|
129
|
-
costUsd: result.usage?.cost_usd || 0,
|
|
130
|
-
model: result.usage?.model || model,
|
|
131
|
-
provider: result.usage?.provider || "sentinelayer",
|
|
132
|
-
latencyMs: result.usage?.latency_ms || 0,
|
|
133
|
-
},
|
|
134
|
-
};
|
|
135
|
-
}
|
|
136
|
-
|
|
137
|
-
export { DEFAULT_PROXY_MODEL };
|
|
1
|
+
/**
|
|
2
|
+
* SentinelLayer LLM proxy provider.
|
|
3
|
+
*
|
|
4
|
+
* Routes LLM calls through POST /api/v1/proxy/llm using the stored
|
|
5
|
+
* sentinelayer_token. Users never need their own OpenAI/Anthropic key.
|
|
6
|
+
*
|
|
7
|
+
* Request: { model, system_prompt, user_content, max_tokens, temperature }
|
|
8
|
+
* Response: { content, usage: { model, provider, tokens_in, tokens_out, cost_usd, latency_ms } }
|
|
9
|
+
*/
|
|
10
|
+
|
|
11
|
+
import { resolveActiveAuthSession } from "../auth/service.js";
|
|
12
|
+
import { authLoginHint } from "../ui/command-hints.js";
|
|
13
|
+
|
|
14
|
+
const DEFAULT_PROXY_MODEL = "gpt-5.3-codex";
|
|
15
|
+
const PROXY_TIMEOUT_MS = 120_000;
|
|
16
|
+
const PROXY_MAX_RETRIES = 2;
|
|
17
|
+
const PROXY_RETRY_STATUSES = new Set([429, 502, 503, 504]);
|
|
18
|
+
|
|
19
|
+
/**
|
|
20
|
+
* Invoke LLM via SentinelLayer proxy.
|
|
21
|
+
*
|
|
22
|
+
* @param {object} options
|
|
23
|
+
* @param {string} options.prompt - The user content / prompt text
|
|
24
|
+
* @param {string} [options.systemPrompt] - System prompt
|
|
25
|
+
* @param {string} [options.model] - Model ID (default: gpt-5.3-codex)
|
|
26
|
+
* @param {number} [options.maxTokens] - Max output tokens (default: 4096)
|
|
27
|
+
* @param {number} [options.temperature] - Temperature (default: 0.1)
|
|
28
|
+
* @param {string} [options.apiUrl] - Override API URL
|
|
29
|
+
* @param {string} [options.token] - Override Bearer token
|
|
30
|
+
* @returns {Promise<{ text: string, usage: { inputTokens: number, outputTokens: number, costUsd: number, model: string, provider: string, latencyMs: number } }>}
|
|
31
|
+
*/
|
|
32
|
+
export async function invokeViaProxy({
|
|
33
|
+
prompt,
|
|
34
|
+
systemPrompt = "",
|
|
35
|
+
model = DEFAULT_PROXY_MODEL,
|
|
36
|
+
maxTokens = 4096,
|
|
37
|
+
temperature = 0.1,
|
|
38
|
+
apiUrl = "",
|
|
39
|
+
token = "",
|
|
40
|
+
} = {}) {
|
|
41
|
+
// Resolve credentials from session if not provided
|
|
42
|
+
let resolvedApiUrl = String(apiUrl || "").trim();
|
|
43
|
+
let resolvedToken = String(token || "").trim();
|
|
44
|
+
|
|
45
|
+
if (!resolvedApiUrl || !resolvedToken) {
|
|
46
|
+
const session = await resolveActiveAuthSession({
|
|
47
|
+
cwd: process.cwd(),
|
|
48
|
+
env: process.env,
|
|
49
|
+
autoRotate: false,
|
|
50
|
+
});
|
|
51
|
+
if (!session || !session.token) {
|
|
52
|
+
throw new Error(
|
|
53
|
+
`SentinelLayer LLM proxy requires authentication. Run '${authLoginHint()}' first.`
|
|
54
|
+
);
|
|
55
|
+
}
|
|
56
|
+
if (!resolvedApiUrl) resolvedApiUrl = String(session.apiUrl || "https://api.sentinelayer.com").trim();
|
|
57
|
+
if (!resolvedToken) resolvedToken = String(session.token).trim();
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
const url = `${resolvedApiUrl.replace(/\/+$/, "")}/api/v1/proxy/llm`;
|
|
61
|
+
|
|
62
|
+
const body = JSON.stringify({
|
|
63
|
+
model,
|
|
64
|
+
system_prompt: systemPrompt || "You are a code reviewer.",
|
|
65
|
+
user_content: String(prompt || ""),
|
|
66
|
+
max_tokens: maxTokens,
|
|
67
|
+
temperature,
|
|
68
|
+
});
|
|
69
|
+
|
|
70
|
+
let response = null;
|
|
71
|
+
let lastError = null;
|
|
72
|
+
|
|
73
|
+
for (let attempt = 0; attempt <= PROXY_MAX_RETRIES; attempt++) {
|
|
74
|
+
try {
|
|
75
|
+
const controller = new AbortController();
|
|
76
|
+
const timeoutHandle = setTimeout(() => controller.abort(), PROXY_TIMEOUT_MS);
|
|
77
|
+
try {
|
|
78
|
+
response = await fetch(url, {
|
|
79
|
+
method: "POST",
|
|
80
|
+
headers: {
|
|
81
|
+
"Content-Type": "application/json",
|
|
82
|
+
Authorization: `Bearer ${resolvedToken}`,
|
|
83
|
+
Accept: "application/json",
|
|
84
|
+
},
|
|
85
|
+
body,
|
|
86
|
+
signal: controller.signal,
|
|
87
|
+
});
|
|
88
|
+
} finally {
|
|
89
|
+
clearTimeout(timeoutHandle);
|
|
90
|
+
}
|
|
91
|
+
|
|
92
|
+
if (PROXY_RETRY_STATUSES.has(response.status) && attempt < PROXY_MAX_RETRIES) {
|
|
93
|
+
const retryAfter = response.headers.get("Retry-After");
|
|
94
|
+
const delay = retryAfter && !isNaN(retryAfter) ? Math.min(Number(retryAfter), 5) * 1000 : 500 * (attempt + 1);
|
|
95
|
+
await new Promise((r) => setTimeout(r, delay));
|
|
96
|
+
continue;
|
|
97
|
+
}
|
|
98
|
+
|
|
99
|
+
break;
|
|
100
|
+
} catch (err) {
|
|
101
|
+
lastError = err;
|
|
102
|
+
if (attempt >= PROXY_MAX_RETRIES) break;
|
|
103
|
+
await new Promise((r) => setTimeout(r, 500 * (attempt + 1)));
|
|
104
|
+
}
|
|
105
|
+
}
|
|
106
|
+
|
|
107
|
+
if (!response) {
|
|
108
|
+
throw new Error(`SentinelLayer LLM proxy request failed: ${lastError?.message || "no response"}`);
|
|
109
|
+
}
|
|
110
|
+
|
|
111
|
+
if (!response.ok) {
|
|
112
|
+
let detail = "";
|
|
113
|
+
try {
|
|
114
|
+
const errBody = await response.json();
|
|
115
|
+
detail = errBody?.error?.message || errBody?.detail || JSON.stringify(errBody).slice(0, 200);
|
|
116
|
+
} catch {
|
|
117
|
+
detail = await response.text().catch(() => "");
|
|
118
|
+
}
|
|
119
|
+
throw new Error(`SentinelLayer LLM proxy error (${response.status}): ${detail}`);
|
|
120
|
+
}
|
|
121
|
+
|
|
122
|
+
const result = await response.json();
|
|
123
|
+
|
|
124
|
+
return {
|
|
125
|
+
text: String(result.content || ""),
|
|
126
|
+
usage: {
|
|
127
|
+
inputTokens: result.usage?.tokens_in || 0,
|
|
128
|
+
outputTokens: result.usage?.tokens_out || 0,
|
|
129
|
+
costUsd: result.usage?.cost_usd || 0,
|
|
130
|
+
model: result.usage?.model || model,
|
|
131
|
+
provider: result.usage?.provider || "sentinelayer",
|
|
132
|
+
latencyMs: result.usage?.latency_ms || 0,
|
|
133
|
+
},
|
|
134
|
+
};
|
|
135
|
+
}
|
|
136
|
+
|
|
137
|
+
export { DEFAULT_PROXY_MODEL };
|
package/src/ai/site-store.js
CHANGED
|
@@ -1,145 +1,145 @@
|
|
|
1
|
-
import fsp from "node:fs/promises";
|
|
2
|
-
import path from "node:path";
|
|
3
|
-
|
|
4
|
-
const REGISTRY_SCHEMA_VERSION = "1.0.0";
|
|
5
|
-
|
|
6
|
-
function normalizeString(value) {
|
|
7
|
-
return String(value || "").trim();
|
|
8
|
-
}
|
|
9
|
-
|
|
10
|
-
function normalizeSiteRecord(record = {}) {
|
|
11
|
-
return {
|
|
12
|
-
siteId: normalizeString(record.siteId),
|
|
13
|
-
projectId: normalizeString(record.projectId) || null,
|
|
14
|
-
identityId: normalizeString(record.identityId) || null,
|
|
15
|
-
domainId: normalizeString(record.domainId) || null,
|
|
16
|
-
host: normalizeString(record.host) || null,
|
|
17
|
-
callbackPath: normalizeString(record.callbackPath) || null,
|
|
18
|
-
callbackUrl: normalizeString(record.callbackUrl) || null,
|
|
19
|
-
status: normalizeString(record.status) || "UNKNOWN",
|
|
20
|
-
dnsCleanupStatus: normalizeString(record.dnsCleanupStatus) || "UNKNOWN",
|
|
21
|
-
expiresAt: normalizeString(record.expiresAt) || null,
|
|
22
|
-
teardownReason: normalizeString(record.teardownReason) || null,
|
|
23
|
-
teardownAt: normalizeString(record.teardownAt) || null,
|
|
24
|
-
createdAt: normalizeString(record.createdAt) || new Date().toISOString(),
|
|
25
|
-
lastUpdatedAt: normalizeString(record.lastUpdatedAt) || new Date().toISOString(),
|
|
26
|
-
dnsCleanupContract:
|
|
27
|
-
record.dnsCleanupContract && typeof record.dnsCleanupContract === "object"
|
|
28
|
-
? record.dnsCleanupContract
|
|
29
|
-
: {},
|
|
30
|
-
metadata: record.metadata && typeof record.metadata === "object" ? record.metadata : {},
|
|
31
|
-
};
|
|
32
|
-
}
|
|
33
|
-
|
|
34
|
-
export function resolveSiteRegistryPath({ outputRoot } = {}) {
|
|
35
|
-
const resolvedOutputRoot = path.resolve(String(outputRoot || "."));
|
|
36
|
-
return path.join(resolvedOutputRoot, "aidenid", "site-registry.json");
|
|
37
|
-
}
|
|
38
|
-
|
|
39
|
-
async function loadRegistryInternal(filePath) {
|
|
40
|
-
try {
|
|
41
|
-
const raw = await fsp.readFile(filePath, "utf-8");
|
|
42
|
-
const parsed = JSON.parse(raw);
|
|
43
|
-
const sites = Array.isArray(parsed.sites)
|
|
44
|
-
? parsed.sites.map((item) => normalizeSiteRecord(item)).filter((item) => item.siteId)
|
|
45
|
-
: [];
|
|
46
|
-
return {
|
|
47
|
-
schemaVersion: normalizeString(parsed.schemaVersion) || REGISTRY_SCHEMA_VERSION,
|
|
48
|
-
generatedAt: normalizeString(parsed.generatedAt) || new Date().toISOString(),
|
|
49
|
-
sites,
|
|
50
|
-
};
|
|
51
|
-
} catch (error) {
|
|
52
|
-
if (error && typeof error === "object" && error.code === "ENOENT") {
|
|
53
|
-
return {
|
|
54
|
-
schemaVersion: REGISTRY_SCHEMA_VERSION,
|
|
55
|
-
generatedAt: new Date().toISOString(),
|
|
56
|
-
sites: [],
|
|
57
|
-
};
|
|
58
|
-
}
|
|
59
|
-
throw error;
|
|
60
|
-
}
|
|
61
|
-
}
|
|
62
|
-
|
|
63
|
-
async function writeRegistryInternal(filePath, registry = {}) {
|
|
64
|
-
const normalized = {
|
|
65
|
-
schemaVersion: REGISTRY_SCHEMA_VERSION,
|
|
66
|
-
generatedAt: new Date().toISOString(),
|
|
67
|
-
sites: Array.isArray(registry.sites) ? registry.sites.map(normalizeSiteRecord) : [],
|
|
68
|
-
};
|
|
69
|
-
await fsp.mkdir(path.dirname(filePath), { recursive: true });
|
|
70
|
-
await fsp.writeFile(filePath, `${JSON.stringify(normalized, null, 2)}\n`, "utf-8");
|
|
71
|
-
return normalized;
|
|
72
|
-
}
|
|
73
|
-
|
|
74
|
-
export async function listSites({ outputRoot, identityId = "" } = {}) {
|
|
75
|
-
const registryPath = resolveSiteRegistryPath({ outputRoot });
|
|
76
|
-
const registry = await loadRegistryInternal(registryPath);
|
|
77
|
-
const normalizedIdentityId = normalizeString(identityId);
|
|
78
|
-
const filteredSites = normalizedIdentityId
|
|
79
|
-
? registry.sites.filter((item) => item.identityId === normalizedIdentityId)
|
|
80
|
-
: registry.sites;
|
|
81
|
-
return {
|
|
82
|
-
registryPath,
|
|
83
|
-
sites: filteredSites,
|
|
84
|
-
};
|
|
85
|
-
}
|
|
86
|
-
|
|
87
|
-
export async function recordTemporarySite({
|
|
88
|
-
outputRoot,
|
|
89
|
-
site = {},
|
|
90
|
-
context = {},
|
|
91
|
-
} = {}) {
|
|
92
|
-
const siteId = normalizeString(site.id || context.siteId);
|
|
93
|
-
if (!siteId) {
|
|
94
|
-
throw new Error("Cannot record temporary site without site.id.");
|
|
95
|
-
}
|
|
96
|
-
|
|
97
|
-
const registryPath = resolveSiteRegistryPath({ outputRoot });
|
|
98
|
-
const registry = await loadRegistryInternal(registryPath);
|
|
99
|
-
const nowIso = new Date().toISOString();
|
|
100
|
-
const nextRecord = normalizeSiteRecord({
|
|
101
|
-
siteId,
|
|
102
|
-
projectId: site.projectId || context.projectId,
|
|
103
|
-
identityId: site.identityId || context.identityId,
|
|
104
|
-
domainId: site.domainId || context.domainId,
|
|
105
|
-
host: site.host,
|
|
106
|
-
callbackPath: site.callbackPath,
|
|
107
|
-
callbackUrl: site.callbackUrl,
|
|
108
|
-
status: site.status,
|
|
109
|
-
dnsCleanupStatus: site.dnsCleanupStatus,
|
|
110
|
-
expiresAt: site.expiresAt,
|
|
111
|
-
teardownReason: site.teardownReason,
|
|
112
|
-
teardownAt: site.teardownAt,
|
|
113
|
-
createdAt: nowIso,
|
|
114
|
-
lastUpdatedAt: nowIso,
|
|
115
|
-
dnsCleanupContract: site.dnsCleanupContract,
|
|
116
|
-
metadata: {
|
|
117
|
-
...(site.metadata && typeof site.metadata === "object" ? site.metadata : {}),
|
|
118
|
-
source: normalizeString(context.source) || "site-create",
|
|
119
|
-
idempotencyKey: context.idempotencyKey || null,
|
|
120
|
-
},
|
|
121
|
-
});
|
|
122
|
-
|
|
123
|
-
const index = registry.sites.findIndex((item) => item.siteId === siteId);
|
|
124
|
-
if (index >= 0) {
|
|
125
|
-
const existing = registry.sites[index];
|
|
126
|
-
registry.sites[index] = normalizeSiteRecord({
|
|
127
|
-
...existing,
|
|
128
|
-
...nextRecord,
|
|
129
|
-
createdAt: existing.createdAt || nextRecord.createdAt,
|
|
130
|
-
metadata: {
|
|
131
|
-
...existing.metadata,
|
|
132
|
-
...nextRecord.metadata,
|
|
133
|
-
},
|
|
134
|
-
});
|
|
135
|
-
} else {
|
|
136
|
-
registry.sites.push(nextRecord);
|
|
137
|
-
}
|
|
138
|
-
|
|
139
|
-
const saved = await writeRegistryInternal(registryPath, registry);
|
|
140
|
-
const savedSite = saved.sites.find((item) => item.siteId === siteId) || null;
|
|
141
|
-
return {
|
|
142
|
-
registryPath,
|
|
143
|
-
site: savedSite,
|
|
144
|
-
};
|
|
145
|
-
}
|
|
1
|
+
import fsp from "node:fs/promises";
|
|
2
|
+
import path from "node:path";
|
|
3
|
+
|
|
4
|
+
const REGISTRY_SCHEMA_VERSION = "1.0.0";
|
|
5
|
+
|
|
6
|
+
function normalizeString(value) {
|
|
7
|
+
return String(value || "").trim();
|
|
8
|
+
}
|
|
9
|
+
|
|
10
|
+
function normalizeSiteRecord(record = {}) {
|
|
11
|
+
return {
|
|
12
|
+
siteId: normalizeString(record.siteId),
|
|
13
|
+
projectId: normalizeString(record.projectId) || null,
|
|
14
|
+
identityId: normalizeString(record.identityId) || null,
|
|
15
|
+
domainId: normalizeString(record.domainId) || null,
|
|
16
|
+
host: normalizeString(record.host) || null,
|
|
17
|
+
callbackPath: normalizeString(record.callbackPath) || null,
|
|
18
|
+
callbackUrl: normalizeString(record.callbackUrl) || null,
|
|
19
|
+
status: normalizeString(record.status) || "UNKNOWN",
|
|
20
|
+
dnsCleanupStatus: normalizeString(record.dnsCleanupStatus) || "UNKNOWN",
|
|
21
|
+
expiresAt: normalizeString(record.expiresAt) || null,
|
|
22
|
+
teardownReason: normalizeString(record.teardownReason) || null,
|
|
23
|
+
teardownAt: normalizeString(record.teardownAt) || null,
|
|
24
|
+
createdAt: normalizeString(record.createdAt) || new Date().toISOString(),
|
|
25
|
+
lastUpdatedAt: normalizeString(record.lastUpdatedAt) || new Date().toISOString(),
|
|
26
|
+
dnsCleanupContract:
|
|
27
|
+
record.dnsCleanupContract && typeof record.dnsCleanupContract === "object"
|
|
28
|
+
? record.dnsCleanupContract
|
|
29
|
+
: {},
|
|
30
|
+
metadata: record.metadata && typeof record.metadata === "object" ? record.metadata : {},
|
|
31
|
+
};
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
export function resolveSiteRegistryPath({ outputRoot } = {}) {
|
|
35
|
+
const resolvedOutputRoot = path.resolve(String(outputRoot || "."));
|
|
36
|
+
return path.join(resolvedOutputRoot, "aidenid", "site-registry.json");
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
async function loadRegistryInternal(filePath) {
|
|
40
|
+
try {
|
|
41
|
+
const raw = await fsp.readFile(filePath, "utf-8");
|
|
42
|
+
const parsed = JSON.parse(raw);
|
|
43
|
+
const sites = Array.isArray(parsed.sites)
|
|
44
|
+
? parsed.sites.map((item) => normalizeSiteRecord(item)).filter((item) => item.siteId)
|
|
45
|
+
: [];
|
|
46
|
+
return {
|
|
47
|
+
schemaVersion: normalizeString(parsed.schemaVersion) || REGISTRY_SCHEMA_VERSION,
|
|
48
|
+
generatedAt: normalizeString(parsed.generatedAt) || new Date().toISOString(),
|
|
49
|
+
sites,
|
|
50
|
+
};
|
|
51
|
+
} catch (error) {
|
|
52
|
+
if (error && typeof error === "object" && error.code === "ENOENT") {
|
|
53
|
+
return {
|
|
54
|
+
schemaVersion: REGISTRY_SCHEMA_VERSION,
|
|
55
|
+
generatedAt: new Date().toISOString(),
|
|
56
|
+
sites: [],
|
|
57
|
+
};
|
|
58
|
+
}
|
|
59
|
+
throw error;
|
|
60
|
+
}
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
async function writeRegistryInternal(filePath, registry = {}) {
|
|
64
|
+
const normalized = {
|
|
65
|
+
schemaVersion: REGISTRY_SCHEMA_VERSION,
|
|
66
|
+
generatedAt: new Date().toISOString(),
|
|
67
|
+
sites: Array.isArray(registry.sites) ? registry.sites.map(normalizeSiteRecord) : [],
|
|
68
|
+
};
|
|
69
|
+
await fsp.mkdir(path.dirname(filePath), { recursive: true });
|
|
70
|
+
await fsp.writeFile(filePath, `${JSON.stringify(normalized, null, 2)}\n`, "utf-8");
|
|
71
|
+
return normalized;
|
|
72
|
+
}
|
|
73
|
+
|
|
74
|
+
export async function listSites({ outputRoot, identityId = "" } = {}) {
|
|
75
|
+
const registryPath = resolveSiteRegistryPath({ outputRoot });
|
|
76
|
+
const registry = await loadRegistryInternal(registryPath);
|
|
77
|
+
const normalizedIdentityId = normalizeString(identityId);
|
|
78
|
+
const filteredSites = normalizedIdentityId
|
|
79
|
+
? registry.sites.filter((item) => item.identityId === normalizedIdentityId)
|
|
80
|
+
: registry.sites;
|
|
81
|
+
return {
|
|
82
|
+
registryPath,
|
|
83
|
+
sites: filteredSites,
|
|
84
|
+
};
|
|
85
|
+
}
|
|
86
|
+
|
|
87
|
+
export async function recordTemporarySite({
|
|
88
|
+
outputRoot,
|
|
89
|
+
site = {},
|
|
90
|
+
context = {},
|
|
91
|
+
} = {}) {
|
|
92
|
+
const siteId = normalizeString(site.id || context.siteId);
|
|
93
|
+
if (!siteId) {
|
|
94
|
+
throw new Error("Cannot record temporary site without site.id.");
|
|
95
|
+
}
|
|
96
|
+
|
|
97
|
+
const registryPath = resolveSiteRegistryPath({ outputRoot });
|
|
98
|
+
const registry = await loadRegistryInternal(registryPath);
|
|
99
|
+
const nowIso = new Date().toISOString();
|
|
100
|
+
const nextRecord = normalizeSiteRecord({
|
|
101
|
+
siteId,
|
|
102
|
+
projectId: site.projectId || context.projectId,
|
|
103
|
+
identityId: site.identityId || context.identityId,
|
|
104
|
+
domainId: site.domainId || context.domainId,
|
|
105
|
+
host: site.host,
|
|
106
|
+
callbackPath: site.callbackPath,
|
|
107
|
+
callbackUrl: site.callbackUrl,
|
|
108
|
+
status: site.status,
|
|
109
|
+
dnsCleanupStatus: site.dnsCleanupStatus,
|
|
110
|
+
expiresAt: site.expiresAt,
|
|
111
|
+
teardownReason: site.teardownReason,
|
|
112
|
+
teardownAt: site.teardownAt,
|
|
113
|
+
createdAt: nowIso,
|
|
114
|
+
lastUpdatedAt: nowIso,
|
|
115
|
+
dnsCleanupContract: site.dnsCleanupContract,
|
|
116
|
+
metadata: {
|
|
117
|
+
...(site.metadata && typeof site.metadata === "object" ? site.metadata : {}),
|
|
118
|
+
source: normalizeString(context.source) || "site-create",
|
|
119
|
+
idempotencyKey: context.idempotencyKey || null,
|
|
120
|
+
},
|
|
121
|
+
});
|
|
122
|
+
|
|
123
|
+
const index = registry.sites.findIndex((item) => item.siteId === siteId);
|
|
124
|
+
if (index >= 0) {
|
|
125
|
+
const existing = registry.sites[index];
|
|
126
|
+
registry.sites[index] = normalizeSiteRecord({
|
|
127
|
+
...existing,
|
|
128
|
+
...nextRecord,
|
|
129
|
+
createdAt: existing.createdAt || nextRecord.createdAt,
|
|
130
|
+
metadata: {
|
|
131
|
+
...existing.metadata,
|
|
132
|
+
...nextRecord.metadata,
|
|
133
|
+
},
|
|
134
|
+
});
|
|
135
|
+
} else {
|
|
136
|
+
registry.sites.push(nextRecord);
|
|
137
|
+
}
|
|
138
|
+
|
|
139
|
+
const saved = await writeRegistryInternal(registryPath, registry);
|
|
140
|
+
const savedSite = saved.sites.find((item) => item.siteId === siteId) || null;
|
|
141
|
+
return {
|
|
142
|
+
registryPath,
|
|
143
|
+
site: savedSite,
|
|
144
|
+
};
|
|
145
|
+
}
|