Save in-progress keysat integration and StartOS 0.4 work
Snapshot of the working tree before cleanup. Captures: - Keysat licensing: server/license.js, /api/license/* endpoints in server/index.js, activation modal in public/index.html, embedded Ed25519 issuer key (assets/issuer.pub). - StartOS 0.4 expansion: setApiKey action, version files v0.1.1 through v0.1.15, file-models/config.json.ts, manifest updates. - Self-hosted registry server (startos-registry/). - Build/deploy scripts (bin/bump-version.sh, bin/deploy.sh, vendored yt-dlp binary), .gitignore, .deploy.env.example. - Recent design docs (KEYSAT_INTEGRATION.md, UPGRADE-DESIGN.md) — retained here so they remain recoverable when removed in the follow-up cleanup commit.
This commit is contained in:
+285
-96
@@ -9,6 +9,7 @@ import os from "os";
|
||||
import https from "https";
|
||||
import http from "http";
|
||||
import { GoogleGenAI } from "@google/genai";
|
||||
import * as license from "./license.js";
|
||||
|
||||
const execFileAsync = promisify(execFile);
|
||||
const app = express();
|
||||
@@ -73,11 +74,9 @@ function ytCookieArgs() {
|
||||
return [];
|
||||
}
|
||||
|
||||
// Extra yt-dlp args for robustness (browser impersonation, rate limiting)
|
||||
// Extra yt-dlp args for robustness (rate limiting)
|
||||
function ytExtraArgs() {
|
||||
const args = [];
|
||||
// Browser impersonation helps avoid TLS fingerprint detection on servers
|
||||
args.push("--impersonate", "chrome");
|
||||
// Sleep between requests to avoid rate limiting during batch operations
|
||||
args.push("--sleep-interval", "1", "--max-sleep-interval", "3");
|
||||
return args;
|
||||
@@ -98,6 +97,125 @@ function resolveApiKey(clientKey) {
|
||||
app.use(cors());
|
||||
app.use(express.json({ limit: "100mb" }));
|
||||
|
||||
// ── Keysat licensing (hard-gate / activate-screen flavor) ─────────────────
|
||||
// All /api/* routes return 402 until a valid license is activated, except
|
||||
// the allowlisted endpoints that exist precisely so the frontend can render
|
||||
// an activation UI. See server/license.js for the verifier.
|
||||
let LIC = license.checkLicense();
|
||||
console.log(
|
||||
`[license] state=${LIC.state} entitlements=[${[...LIC.entitlements].join(",")}]` +
|
||||
(LIC.reason ? ` reason=${LIC.reason}` : "")
|
||||
);
|
||||
|
||||
// Endpoints reachable without a license — kept intentionally minimal.
|
||||
const LICENSE_OPEN_PATHS = new Set([
|
||||
"/api/health",
|
||||
"/api/heartbeat",
|
||||
"/api/status",
|
||||
"/api/license-status",
|
||||
"/api/license/activate",
|
||||
"/api/license/deactivate",
|
||||
]);
|
||||
|
||||
// Activation-screen gate: any /api/* request without a valid license is
|
||||
// rejected with 402, except the allowlist above. Non-/api requests
|
||||
// (the static frontend, /assets, etc.) pass through so the UI can load.
|
||||
app.use((req, res, next) => {
|
||||
if (!req.path.startsWith("/api/")) return next();
|
||||
if (LICENSE_OPEN_PATHS.has(req.path)) return next();
|
||||
if (LIC.state === "licensed" && LIC.entitlements.has("core")) return next();
|
||||
return res.status(402).json({
|
||||
error: "license_required",
|
||||
message:
|
||||
LIC.state === "licensed"
|
||||
? "Your license is missing the 'core' entitlement. Contact the seller."
|
||||
: "This service requires a Keysat license. Activate to continue.",
|
||||
state: LIC.state,
|
||||
reason: LIC.reason,
|
||||
activate_url: "/#activate",
|
||||
keysat_base_url: license.KEYSAT_BASE_URL,
|
||||
product_slug: license.PRODUCT_SLUG,
|
||||
});
|
||||
});
|
||||
|
||||
// Pro-tier feature gates. Each entry maps URL prefixes → required
|
||||
// entitlement; first match wins. A licensed user without the right
|
||||
// entitlement gets a clean 402 feature_not_in_tier (vs. the generic
|
||||
// activation gate above).
|
||||
const PRO_FEATURE_GATES = [
|
||||
{
|
||||
prefixes: ["/api/subscriptions", "/api/auto-queue", "/api/sub-check-log"],
|
||||
entitlement: "subscriptions",
|
||||
feature: "subscriptions",
|
||||
message:
|
||||
"Channel subscriptions and auto-queue require a Pro license. Upgrade to unlock.",
|
||||
},
|
||||
{
|
||||
prefixes: ["/api/history"],
|
||||
entitlement: "history",
|
||||
feature: "history",
|
||||
message:
|
||||
"Summary history requires a Pro license. Upgrade to unlock.",
|
||||
},
|
||||
{
|
||||
prefixes: ["/api/library"],
|
||||
entitlement: "library",
|
||||
feature: "library",
|
||||
message:
|
||||
"Library import/export requires a Pro license. Upgrade to unlock.",
|
||||
},
|
||||
];
|
||||
app.use((req, res, next) => {
|
||||
for (const gate of PRO_FEATURE_GATES) {
|
||||
if (gate.prefixes.some((p) => req.path.startsWith(p))) {
|
||||
if (LIC.entitlements.has(gate.entitlement)) return next();
|
||||
return res.status(402).json({
|
||||
error: "feature_not_in_tier",
|
||||
feature: gate.feature,
|
||||
message: gate.message,
|
||||
keysat_base_url: license.KEYSAT_BASE_URL,
|
||||
product_slug: license.PRODUCT_SLUG,
|
||||
});
|
||||
}
|
||||
}
|
||||
next();
|
||||
});
|
||||
|
||||
// License management endpoints — kept open by LICENSE_OPEN_PATHS above.
|
||||
app.get("/api/license-status", (_req, res) => {
|
||||
res.json(license.publicView(LIC));
|
||||
});
|
||||
|
||||
app.post("/api/license/activate", (req, res) => {
|
||||
try {
|
||||
LIC = license.activate(req.body && req.body.license_key);
|
||||
} catch (e) {
|
||||
if (e && e.code === "bad_format") {
|
||||
return res.status(400).json({
|
||||
error: "bad_format",
|
||||
message: "Expected a license key starting with 'LIC1-'.",
|
||||
});
|
||||
}
|
||||
return res.status(500).json({ error: "activation_failed", message: e?.message });
|
||||
}
|
||||
if (LIC.state === "licensed") {
|
||||
return res.json({ ok: true, ...license.publicView(LIC) });
|
||||
}
|
||||
return res.status(400).json({
|
||||
ok: false,
|
||||
error: "invalid",
|
||||
...license.publicView(LIC),
|
||||
});
|
||||
});
|
||||
|
||||
app.post("/api/license/deactivate", async (_req, res) => {
|
||||
try {
|
||||
await fs.unlink(license.LICENSE_PATH).catch(() => {});
|
||||
} catch {}
|
||||
LIC = license.checkLicense();
|
||||
res.json({ ok: true, ...license.publicView(LIC) });
|
||||
});
|
||||
|
||||
// ── History storage ───────────────────────────────────────────────────────
|
||||
|
||||
async function saveToHistory(videoId, url, title, chunks, entries, logs, uploadDate, type) {
|
||||
@@ -401,95 +519,11 @@ app.get("/api/cookies/status", async (req, res) => {
|
||||
res.json(info);
|
||||
});
|
||||
|
||||
// ── OAuth2 YouTube authentication (headless / StartOS) ────────────────────
|
||||
// Uses yt-dlp's built-in OAuth2 device flow: user enters a code on any browser,
|
||||
// token is cached in yt-dlp's cache dir (persisted on /data volume).
|
||||
|
||||
let oauthInProgress = false;
|
||||
let oauthDeviceCode = null;
|
||||
let oauthVerifyUrl = null;
|
||||
|
||||
// Check if OAuth token already exists in yt-dlp cache
|
||||
app.get("/api/auth/oauth/status", async (req, res) => {
|
||||
// yt-dlp stores OAuth tokens in its cache dir
|
||||
const cacheDir = process.env.XDG_CACHE_HOME || path.join(DATA_DIR, "ytdlp-cache");
|
||||
let hasToken = false;
|
||||
try {
|
||||
const files = await fs.readdir(path.join(cacheDir, "yt-dlp"), { recursive: true }).catch(() => []);
|
||||
hasToken = files.some(f => f.includes("oauth") || f.includes("token"));
|
||||
} catch {}
|
||||
|
||||
res.json({
|
||||
hasToken,
|
||||
hasCookies: ytCookiesFileExists,
|
||||
cookieMethod: ytCookieMethod(),
|
||||
inProgress: oauthInProgress,
|
||||
deviceCode: oauthInProgress ? oauthDeviceCode : null,
|
||||
verifyUrl: oauthInProgress ? oauthVerifyUrl : null,
|
||||
});
|
||||
});
|
||||
|
||||
// Initiate OAuth2 device code flow
|
||||
app.post("/api/auth/oauth/start", async (req, res) => {
|
||||
if (oauthInProgress) {
|
||||
return res.json({ ok: true, message: "OAuth flow already in progress", deviceCode: oauthDeviceCode, verifyUrl: oauthVerifyUrl });
|
||||
}
|
||||
|
||||
oauthInProgress = true;
|
||||
oauthDeviceCode = null;
|
||||
oauthVerifyUrl = null;
|
||||
|
||||
res.json({ ok: true, message: "OAuth flow started. Check /api/auth/oauth/status for the device code." });
|
||||
|
||||
// Run yt-dlp in background — it will output the device code to stderr
|
||||
(async () => {
|
||||
try {
|
||||
const cacheDir = path.join(DATA_DIR, "ytdlp-cache");
|
||||
await fs.mkdir(cacheDir, { recursive: true });
|
||||
|
||||
const proc = execFile("yt-dlp", [
|
||||
"--username", "oauth",
|
||||
"--password", "",
|
||||
"--cache-dir", path.join(cacheDir, "yt-dlp"),
|
||||
"--print", "%(title)s",
|
||||
"--no-download",
|
||||
"https://www.youtube.com/watch?v=dQw4w9WgXcQ",
|
||||
], { timeout: 300000, env: { ...process.env, XDG_CACHE_HOME: cacheDir } });
|
||||
|
||||
let stderrBuf = "";
|
||||
proc.stderr?.on("data", (chunk) => {
|
||||
stderrBuf += chunk.toString();
|
||||
// Look for the device code pattern in yt-dlp output
|
||||
const codeMatch = stderrBuf.match(/go to\s+(https?:\/\/\S+)\s+.*?enter.*?code[:\s]+([A-Z0-9-]+)/i);
|
||||
if (codeMatch) {
|
||||
oauthVerifyUrl = codeMatch[1];
|
||||
oauthDeviceCode = codeMatch[2];
|
||||
console.log(` 🔑 OAuth device code: ${oauthDeviceCode} — verify at ${oauthVerifyUrl}`);
|
||||
}
|
||||
});
|
||||
|
||||
await new Promise((resolve, reject) => {
|
||||
proc.on("close", (code) => {
|
||||
if (code === 0) resolve();
|
||||
else reject(new Error(`yt-dlp exited with code ${code}: ${stderrBuf.slice(-300)}`));
|
||||
});
|
||||
proc.on("error", reject);
|
||||
});
|
||||
|
||||
console.log(" ✓ OAuth token cached successfully");
|
||||
} catch (err) {
|
||||
console.error(" ⚠ OAuth flow failed:", err.message);
|
||||
} finally {
|
||||
oauthInProgress = false;
|
||||
}
|
||||
})();
|
||||
});
|
||||
|
||||
// ── History endpoints ─────────────────────────────────────────────────────
|
||||
|
||||
const metaPath = path.join(historyDir, "_meta.json");
|
||||
|
||||
// meta.json structure: { folders: [ { id, name, order, items: [sessionId, ...] } ], uncategorized: [sessionId, ...] }
|
||||
// meta.json structure: { folders: [ { id, name, order, collapsed, items: [sessionId, ...] } ], uncategorized: [sessionId, ...] }
|
||||
async function loadMeta() {
|
||||
try {
|
||||
return JSON.parse(await fs.readFile(metaPath, "utf-8"));
|
||||
@@ -615,7 +649,7 @@ app.put("/api/history/meta", async (req, res) => {
|
||||
app.post("/api/history/folders", async (req, res) => {
|
||||
try {
|
||||
const meta = await loadMeta();
|
||||
const folder = { id: `folder-${Date.now()}`, name: req.body.name || "New Folder", items: [] };
|
||||
const folder = { id: `folder-${Date.now()}`, name: req.body.name || "New Folder", collapsed: false, items: [] };
|
||||
meta.folders.push(folder);
|
||||
await saveMeta(meta);
|
||||
res.json(folder);
|
||||
@@ -638,6 +672,20 @@ app.put("/api/history/folders/:id", async (req, res) => {
|
||||
}
|
||||
});
|
||||
|
||||
// Update a folder's collapsed state
|
||||
app.put("/api/history/folders/:id/collapsed", async (req, res) => {
|
||||
try {
|
||||
const meta = await loadMeta();
|
||||
const folder = meta.folders.find(f => f.id === req.params.id);
|
||||
if (!folder) return res.status(404).json({ error: "Folder not found" });
|
||||
folder.collapsed = !!req.body.collapsed;
|
||||
await saveMeta(meta);
|
||||
res.json({ ok: true, collapsed: folder.collapsed });
|
||||
} catch (err) {
|
||||
res.status(500).json({ error: err.message });
|
||||
}
|
||||
});
|
||||
|
||||
// Delete a folder (items move to uncategorized)
|
||||
app.delete("/api/history/folders/:id", async (req, res) => {
|
||||
try {
|
||||
@@ -684,6 +732,119 @@ app.put("/api/history/move", async (req, res) => {
|
||||
}
|
||||
});
|
||||
|
||||
// ── Library export/import ────────────────────────────────────────────────
|
||||
|
||||
app.get("/api/library/export", async (req, res) => {
|
||||
try {
|
||||
const meta = await loadMeta();
|
||||
const files = await fs.readdir(historyDir);
|
||||
const sessions = {};
|
||||
for (const file of files) {
|
||||
if (!file.endsWith(".json") || file === "_meta.json" || file === "subscriptions.json" || file === "auto-queue.json") continue;
|
||||
try {
|
||||
const raw = await fs.readFile(path.join(historyDir, file), "utf-8");
|
||||
const id = file.replace(".json", "");
|
||||
sessions[id] = JSON.parse(raw);
|
||||
} catch {}
|
||||
}
|
||||
// Load subscriptions
|
||||
let subscriptions = [];
|
||||
try {
|
||||
subscriptions = JSON.parse(await fs.readFile(path.join(historyDir, "subscriptions.json"), "utf-8")).subscriptions || [];
|
||||
} catch {}
|
||||
|
||||
const exportData = {
|
||||
version: 1,
|
||||
exportedAt: new Date().toISOString(),
|
||||
meta,
|
||||
sessions,
|
||||
subscriptions,
|
||||
};
|
||||
res.setHeader("Content-Type", "application/json");
|
||||
res.setHeader("Content-Disposition", 'attachment; filename="youtube-summarizer-library.json"');
|
||||
res.json(exportData);
|
||||
} catch (err) {
|
||||
res.status(500).json({ error: err.message });
|
||||
}
|
||||
});
|
||||
|
||||
app.post("/api/library/import", express.json({ limit: "200mb" }), async (req, res) => {
|
||||
try {
|
||||
const data = req.body;
|
||||
if (!data || !data.sessions) {
|
||||
return res.status(400).json({ error: "Invalid library file — missing sessions data" });
|
||||
}
|
||||
|
||||
let imported = 0;
|
||||
let skipped = 0;
|
||||
|
||||
// Import sessions
|
||||
for (const [id, session] of Object.entries(data.sessions)) {
|
||||
const filePath = path.join(historyDir, `${id}.json`);
|
||||
// Skip if session already exists (don't overwrite)
|
||||
try {
|
||||
await fs.access(filePath);
|
||||
skipped++;
|
||||
continue;
|
||||
} catch {}
|
||||
await fs.writeFile(filePath, JSON.stringify(session));
|
||||
imported++;
|
||||
}
|
||||
|
||||
// Merge meta (add imported sessions to uncategorized if not already placed)
|
||||
if (data.meta) {
|
||||
const existingMeta = await loadMeta();
|
||||
const allExistingIds = new Set([
|
||||
...existingMeta.uncategorized,
|
||||
...existingMeta.folders.flatMap(f => f.items),
|
||||
]);
|
||||
|
||||
// Import folders that don't exist
|
||||
if (data.meta.folders) {
|
||||
for (const folder of data.meta.folders) {
|
||||
const existingFolder = existingMeta.folders.find(f => f.id === folder.id);
|
||||
if (!existingFolder) {
|
||||
existingMeta.folders.push(folder);
|
||||
folder.items.forEach(id => allExistingIds.add(id));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Add any uncategorized items that aren't already placed
|
||||
if (data.meta.uncategorized) {
|
||||
for (const id of data.meta.uncategorized) {
|
||||
if (!allExistingIds.has(id)) {
|
||||
existingMeta.uncategorized.unshift(id);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
await saveMeta(existingMeta);
|
||||
}
|
||||
|
||||
// Import subscriptions (merge, don't duplicate)
|
||||
if (data.subscriptions && data.subscriptions.length > 0) {
|
||||
let existingSubs = [];
|
||||
try {
|
||||
existingSubs = JSON.parse(await fs.readFile(path.join(historyDir, "subscriptions.json"), "utf-8")).subscriptions || [];
|
||||
} catch {}
|
||||
const existingUrls = new Set(existingSubs.map(s => s.url));
|
||||
let subsAdded = 0;
|
||||
for (const sub of data.subscriptions) {
|
||||
if (!existingUrls.has(sub.url)) {
|
||||
existingSubs.push(sub);
|
||||
subsAdded++;
|
||||
}
|
||||
}
|
||||
await fs.writeFile(path.join(historyDir, "subscriptions.json"), JSON.stringify({ subscriptions: existingSubs }));
|
||||
}
|
||||
|
||||
res.json({ ok: true, imported, skipped, total: Object.keys(data.sessions).length });
|
||||
} catch (err) {
|
||||
res.status(500).json({ error: err.message });
|
||||
}
|
||||
});
|
||||
|
||||
// ── Subscriptions ─────────────────────────────────────────────────────────
|
||||
|
||||
const subsPath = path.join(historyDir, "subscriptions.json");
|
||||
@@ -949,6 +1110,20 @@ async function fetchChannelName(url) {
|
||||
return "Unknown Channel";
|
||||
}
|
||||
|
||||
// ── In-process write serialization ────────────────────────────────────────
|
||||
// Per-file promise chain to prevent lost-update races on read-modify-write
|
||||
// state files (skip-list, seen-list). Without this, two concurrent DELETE
|
||||
// handlers can each load the same snapshot, add their own id, and the
|
||||
// second write overwrites the first — silently dropping entries.
|
||||
const _fileLocks = new Map();
|
||||
function withFileLock(key, fn) {
|
||||
const prev = _fileLocks.get(key) || Promise.resolve();
|
||||
const next = prev.then(fn, fn); // run fn whether prev resolved or rejected
|
||||
// Keep the chain alive but don't leak errors to the next caller
|
||||
_fileLocks.set(key, next.catch(() => {}));
|
||||
return next;
|
||||
}
|
||||
|
||||
// Skip list — videos deleted from history that subscriptions should not re-add
|
||||
const skipPath = path.join(historyDir, "skip-list.json");
|
||||
|
||||
@@ -961,9 +1136,11 @@ async function loadSkipList() {
|
||||
}
|
||||
|
||||
async function addToSkipList(videoId) {
|
||||
const skipIds = await loadSkipList();
|
||||
skipIds.add(videoId);
|
||||
await fs.writeFile(skipPath, JSON.stringify({ videoIds: [...skipIds] }));
|
||||
return withFileLock(skipPath, async () => {
|
||||
const skipIds = await loadSkipList();
|
||||
skipIds.add(videoId);
|
||||
await fs.writeFile(skipPath, JSON.stringify({ videoIds: [...skipIds] }));
|
||||
});
|
||||
}
|
||||
|
||||
// Seen list — videos already offered for approval (persists across restarts)
|
||||
@@ -978,9 +1155,11 @@ async function loadSeenList() {
|
||||
}
|
||||
|
||||
async function addToSeenList(videoIds) {
|
||||
const seen = await loadSeenList();
|
||||
for (const id of videoIds) seen.add(id);
|
||||
await fs.writeFile(seenPath, JSON.stringify({ videoIds: [...seen] }));
|
||||
return withFileLock(seenPath, async () => {
|
||||
const seen = await loadSeenList();
|
||||
for (const id of videoIds) seen.add(id);
|
||||
await fs.writeFile(seenPath, JSON.stringify({ videoIds: [...seen] }));
|
||||
});
|
||||
}
|
||||
|
||||
// Get all videoIds already processed in history
|
||||
@@ -1236,6 +1415,13 @@ async function checkSubscriptions() {
|
||||
}
|
||||
|
||||
async function _checkSubscriptionsInner() {
|
||||
// Pro-tier feature: skip silently when not entitled. The HTTP gate above
|
||||
// returns 402 to callers; this guards the background timer + manual paths.
|
||||
if (!LIC.entitlements.has("subscriptions")) {
|
||||
subCheckLog = [];
|
||||
subLog("Skipped: subscriptions require a Pro license.");
|
||||
return;
|
||||
}
|
||||
subCheckLog = []; // Clear logs for fresh check
|
||||
const subs = await loadSubscriptions();
|
||||
if (subs.length === 0) { subLog("No subscriptions found"); return; }
|
||||
@@ -1503,7 +1689,7 @@ function channelKeyFromUrl(url) {
|
||||
}
|
||||
|
||||
app.post("/api/subscriptions", async (req, res) => {
|
||||
const { url, since, type } = req.body;
|
||||
const { url, since, type, autoDownload } = req.body;
|
||||
if (!url) return res.status(400).json({ error: "Missing url" });
|
||||
|
||||
const isPodcast = type === "podcast" || isPodcastFeedUrl(url);
|
||||
@@ -1534,6 +1720,7 @@ app.post("/api/subscriptions", async (req, res) => {
|
||||
createdAt: cutoff,
|
||||
lastChecked: null,
|
||||
paused: false,
|
||||
autoDownload: autoDownload === true,
|
||||
};
|
||||
subs.push(sub);
|
||||
await saveSubscriptions(subs);
|
||||
@@ -1762,6 +1949,8 @@ app.get("/api/processing/log", (req, res) => {
|
||||
|
||||
app.post("/api/process", async (req, res) => {
|
||||
const { url, apiKey: clientKey, model, type: itemType, title: itemTitle, uploadDate: itemUploadDate, episodeId } = req.body;
|
||||
// BYO Gemini key is a Core-tier feature; the activation gate already
|
||||
// ensures the caller is licensed, so no further check is needed here.
|
||||
const apiKey = resolveApiKey(clientKey);
|
||||
|
||||
if (!url) {
|
||||
|
||||
@@ -0,0 +1,145 @@
|
||||
// ── Keysat license verification ──────────────────────────────────────────
|
||||
//
|
||||
// Reads a LIC1-... key from disk (or env), verifies its Ed25519 signature
|
||||
// against the operator's embedded public key, and exposes the resulting
|
||||
// state + entitlement set to the rest of the server.
|
||||
//
|
||||
// Operator config — keep these three constants in sync with what's set in
|
||||
// the Keysat admin UI:
|
||||
// ISSUER_PEM → assets/issuer.pub (committed; non-secret)
|
||||
// PRODUCT_SLUG → must match the product slug created in Keysat
|
||||
// KEYSAT_BASE_URL → optional, only used by online validate() / purchase
|
||||
//
|
||||
// Tier model for this app (see KEYSAT_INTEGRATION.md §0):
|
||||
// "core" — required for any business endpoint; unlocks
|
||||
// summarization and BYO Gemini API key
|
||||
// "history" — saved summary library: /api/history*
|
||||
// "library" — bulk import/export: /api/library/*
|
||||
// "subscriptions" — Pro: channel subs, auto-queue, sub-check log
|
||||
// "clips" — Pro: paperclip / clip-collection panel
|
||||
//
|
||||
// Tier policies:
|
||||
// Core → ["core", "history", "library"]
|
||||
// Pro → ["core", "history", "library", "subscriptions", "clips"]
|
||||
|
||||
import fs from "fs";
|
||||
import path from "path";
|
||||
import { Verifier, PublicKey } from "@keysat/licensing-client";
|
||||
|
||||
export const PRODUCT_SLUG = "youtube-summarizer";
|
||||
export const KEYSAT_BASE_URL = "https://licensing.keysat.xyz";
|
||||
|
||||
const __dirname = path.dirname(new URL(import.meta.url).pathname);
|
||||
const PEM_PATH = path.join(__dirname, "..", "assets", "issuer.pub");
|
||||
const ISSUER_PEM = fs.readFileSync(PEM_PATH, "utf8");
|
||||
|
||||
// License file lives next to existing config/ and history/ in DATA_DIR.
|
||||
// On StartOS that's /data; on local Mac dev it's the project root.
|
||||
const DATA_DIR = process.env.DATA_DIR || path.join(__dirname, "..");
|
||||
export const LICENSE_PATH =
|
||||
process.env.YT_SUMMARIZER_LICENSE_KEY_PATH ||
|
||||
path.join(DATA_DIR, "license.txt");
|
||||
|
||||
// ── Verifier instance (built once at module load) ─────────────────────────
|
||||
let verifier = null;
|
||||
let verifierError = null;
|
||||
try {
|
||||
verifier = new Verifier(PublicKey.fromPem(ISSUER_PEM));
|
||||
} catch (e) {
|
||||
verifierError = e?.message || String(e);
|
||||
console.error(`[license] failed to parse embedded public key: ${verifierError}`);
|
||||
}
|
||||
|
||||
// ── Helpers ───────────────────────────────────────────────────────────────
|
||||
function readLicenseString() {
|
||||
const fromEnv = (process.env.YT_SUMMARIZER_LICENSE_KEY || "").trim();
|
||||
if (fromEnv) return fromEnv;
|
||||
try {
|
||||
const s = fs.readFileSync(LICENSE_PATH, "utf8").trim();
|
||||
return s || null;
|
||||
} catch {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
function emptyState(extra = {}) {
|
||||
return {
|
||||
state: "unlicensed",
|
||||
reason: null,
|
||||
licenseId: null,
|
||||
entitlements: new Set(),
|
||||
expiresAt: null,
|
||||
isTrial: false,
|
||||
...extra,
|
||||
};
|
||||
}
|
||||
|
||||
// ── Public API ────────────────────────────────────────────────────────────
|
||||
//
|
||||
// checkLicense() — read + verify; returns a frozen-ish state object.
|
||||
// Callers can re-invoke after activation to refresh.
|
||||
export function checkLicense() {
|
||||
if (verifierError) {
|
||||
return emptyState({ state: "invalid", reason: `bad embedded key: ${verifierError}` });
|
||||
}
|
||||
const raw = readLicenseString();
|
||||
if (!raw) return emptyState();
|
||||
|
||||
try {
|
||||
const ok = verifier.verify(raw);
|
||||
const payload = ok.payload || {};
|
||||
// Reject keys minted for a different product (same operator, different SKU).
|
||||
if (payload.productSlug && payload.productSlug !== PRODUCT_SLUG) {
|
||||
return emptyState({ state: "invalid", reason: "product_mismatch" });
|
||||
}
|
||||
return {
|
||||
state: "licensed",
|
||||
reason: null,
|
||||
licenseId: payload.licenseId || null,
|
||||
entitlements: new Set(payload.entitlements || []),
|
||||
expiresAt: payload.expiresAt ? new Date(payload.expiresAt * 1000) : null,
|
||||
isTrial: !!(payload.flags & 1),
|
||||
};
|
||||
} catch (e) {
|
||||
return emptyState({ state: "invalid", reason: e?.message || "verify_failed" });
|
||||
}
|
||||
}
|
||||
|
||||
// activate(rawKey) — write a pasted key to disk, then re-check.
|
||||
// Returns the new license state. Throws on bad input format only;
|
||||
// signature failures surface as state: 'invalid' with a reason.
|
||||
export function activate(rawKey) {
|
||||
const key = (rawKey || "").trim();
|
||||
if (!key.startsWith("LIC1-")) {
|
||||
const err = new Error("bad_format");
|
||||
err.code = "bad_format";
|
||||
throw err;
|
||||
}
|
||||
// Write atomically-ish: write to temp file then rename.
|
||||
const tmp = LICENSE_PATH + ".tmp";
|
||||
fs.mkdirSync(path.dirname(LICENSE_PATH), { recursive: true });
|
||||
fs.writeFileSync(tmp, key + "\n", { mode: 0o600 });
|
||||
fs.renameSync(tmp, LICENSE_PATH);
|
||||
return checkLicense();
|
||||
}
|
||||
|
||||
// publicView(state) — safe shape for /api/license-status responses.
|
||||
// Never leaks the raw license key (it's a bearer credential).
|
||||
export function publicView(state) {
|
||||
return {
|
||||
state: state.state,
|
||||
reason: state.reason,
|
||||
licenseId: state.licenseId,
|
||||
entitlements: [...state.entitlements].sort(),
|
||||
expiresAt: state.expiresAt ? state.expiresAt.toISOString() : null,
|
||||
isTrial: !!state.isTrial,
|
||||
productSlug: PRODUCT_SLUG,
|
||||
keysatBaseUrl: KEYSAT_BASE_URL,
|
||||
licensePath: LICENSE_PATH,
|
||||
};
|
||||
}
|
||||
|
||||
// has(state, entitlement) — convenience wrapper for feature gates.
|
||||
export function has(state, entitlement) {
|
||||
return state && state.entitlements && state.entitlements.has(entitlement);
|
||||
}
|
||||
Generated
+110
-562
@@ -9,18 +9,20 @@
|
||||
"version": "1.0.0",
|
||||
"dependencies": {
|
||||
"@google/genai": "^1.41.0",
|
||||
"@keysat/licensing-client": "git+https://github.com/keysat-xyz/keysat-client-ts.git",
|
||||
"cors": "^2.8.5",
|
||||
"express": "^4.21.0"
|
||||
}
|
||||
},
|
||||
"node_modules/@google/genai": {
|
||||
"version": "1.41.0",
|
||||
"resolved": "https://registry.npmjs.org/@google/genai/-/genai-1.41.0.tgz",
|
||||
"integrity": "sha512-S4WGil+PG0NBQRAx+0yrQuM/TWOLn2gGEy5wn4IsoOI6ouHad0P61p3OWdhJ3aqr9kfj8o904i/jevfaGoGuIQ==",
|
||||
"version": "1.52.0",
|
||||
"resolved": "https://registry.npmjs.org/@google/genai/-/genai-1.52.0.tgz",
|
||||
"integrity": "sha512-gwSvbpiN/17O9TbsqSsE/OzZcpv5Fo4RQjdngGgogtuB9RsyJ8ZHhX5KjHj1bp5N9snN2eK8LDGXSaWW2hof8Q==",
|
||||
"hasInstallScript": true,
|
||||
"license": "Apache-2.0",
|
||||
"dependencies": {
|
||||
"google-auth-library": "^10.3.0",
|
||||
"p-retry": "^7.1.1",
|
||||
"p-retry": "^4.6.2",
|
||||
"protobufjs": "^7.5.4",
|
||||
"ws": "^8.18.0"
|
||||
},
|
||||
@@ -36,31 +38,37 @@
|
||||
}
|
||||
}
|
||||
},
|
||||
"node_modules/@isaacs/cliui": {
|
||||
"version": "8.0.2",
|
||||
"resolved": "https://registry.npmjs.org/@isaacs/cliui/-/cliui-8.0.2.tgz",
|
||||
"integrity": "sha512-O8jcjabXaleOG9DQ0+ARXWZBTfnP4WNAqzuiJK7ll44AmxGKv/J2M4TPjxjY3znBCfvBXFzucm1twdyFybFqEA==",
|
||||
"license": "ISC",
|
||||
"node_modules/@keysat/licensing-client": {
|
||||
"version": "0.1.0",
|
||||
"resolved": "git+ssh://git@github.com/keysat-xyz/keysat-client-ts.git#61a6518ac80b08d672a53c5be3a31cf2064cd0ff",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"string-width": "^5.1.2",
|
||||
"string-width-cjs": "npm:string-width@^4.2.0",
|
||||
"strip-ansi": "^7.0.1",
|
||||
"strip-ansi-cjs": "npm:strip-ansi@^6.0.1",
|
||||
"wrap-ansi": "^8.1.0",
|
||||
"wrap-ansi-cjs": "npm:wrap-ansi@^7.0.0"
|
||||
"@noble/ed25519": "^2.0.0",
|
||||
"@noble/hashes": "^1.3.3"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">=12"
|
||||
"node": ">=18"
|
||||
}
|
||||
},
|
||||
"node_modules/@pkgjs/parseargs": {
|
||||
"version": "0.11.0",
|
||||
"resolved": "https://registry.npmjs.org/@pkgjs/parseargs/-/parseargs-0.11.0.tgz",
|
||||
"integrity": "sha512-+1VkjdD0QBLPodGrJUeqarH8VAIvQODIbwh9XpP5Syisf7YoQgsJKPNFoqqLQlu+VQ/tVSshMR6loPMn8U+dPg==",
|
||||
"node_modules/@noble/ed25519": {
|
||||
"version": "2.3.0",
|
||||
"resolved": "https://registry.npmjs.org/@noble/ed25519/-/ed25519-2.3.0.tgz",
|
||||
"integrity": "sha512-M7dvXL2B92/M7dw9+gzuydL8qn/jiqNHaoR3Q+cb1q1GHV7uwE17WCyFMG+Y+TZb5izcaXk5TdJRrDUxHXL78A==",
|
||||
"license": "MIT",
|
||||
"funding": {
|
||||
"url": "https://paulmillr.com/funding/"
|
||||
}
|
||||
},
|
||||
"node_modules/@noble/hashes": {
|
||||
"version": "1.8.0",
|
||||
"resolved": "https://registry.npmjs.org/@noble/hashes/-/hashes-1.8.0.tgz",
|
||||
"integrity": "sha512-jCs9ldd7NwzpgXDIf6P3+NrHh9/sD6CQdxHyjQI+h/6rDNo88ypBxxz45UDuZHz9r3tNz7N/VInSVoVdtXEI4A==",
|
||||
"license": "MIT",
|
||||
"optional": true,
|
||||
"engines": {
|
||||
"node": ">=14"
|
||||
"node": "^14.21.3 || >=16"
|
||||
},
|
||||
"funding": {
|
||||
"url": "https://paulmillr.com/funding/"
|
||||
}
|
||||
},
|
||||
"node_modules/@protobufjs/aspromise": {
|
||||
@@ -76,9 +84,9 @@
|
||||
"license": "BSD-3-Clause"
|
||||
},
|
||||
"node_modules/@protobufjs/codegen": {
|
||||
"version": "2.0.4",
|
||||
"resolved": "https://registry.npmjs.org/@protobufjs/codegen/-/codegen-2.0.4.tgz",
|
||||
"integrity": "sha512-YyFaikqM5sH0ziFZCN3xDC7zeGaB/d0IUb9CATugHWbd1FRFwWwt4ld4OYMPWu5a3Xe01mGAULCdqhMlPl29Jg==",
|
||||
"version": "2.0.5",
|
||||
"resolved": "https://registry.npmjs.org/@protobufjs/codegen/-/codegen-2.0.5.tgz",
|
||||
"integrity": "sha512-zgXFLzW3Ap33e6d0Wlj4MGIm6Ce8O89n/apUaGNB/jx+hw+ruWEp7EwGUshdLKVRCxZW12fp9r40E1mQrf/34g==",
|
||||
"license": "BSD-3-Clause"
|
||||
},
|
||||
"node_modules/@protobufjs/eventemitter": {
|
||||
@@ -104,9 +112,9 @@
|
||||
"license": "BSD-3-Clause"
|
||||
},
|
||||
"node_modules/@protobufjs/inquire": {
|
||||
"version": "1.1.0",
|
||||
"resolved": "https://registry.npmjs.org/@protobufjs/inquire/-/inquire-1.1.0.tgz",
|
||||
"integrity": "sha512-kdSefcPdruJiFMVSbn801t4vFK7KB/5gd2fYvrxhuJYg8ILrmn9SKSX2tZdV6V+ksulWqS7aXjBcRXl3wHoD9Q==",
|
||||
"version": "1.1.1",
|
||||
"resolved": "https://registry.npmjs.org/@protobufjs/inquire/-/inquire-1.1.1.tgz",
|
||||
"integrity": "sha512-mnzgDV26ueAvk7rsbt9L7bE0SuAoqyuys/sMMrmVcN5x9VsxpcG3rqAUSgDyLp0UZlmNfIbQ4fHfCtreVBk8Ew==",
|
||||
"license": "BSD-3-Clause"
|
||||
},
|
||||
"node_modules/@protobufjs/path": {
|
||||
@@ -122,20 +130,26 @@
|
||||
"license": "BSD-3-Clause"
|
||||
},
|
||||
"node_modules/@protobufjs/utf8": {
|
||||
"version": "1.1.0",
|
||||
"resolved": "https://registry.npmjs.org/@protobufjs/utf8/-/utf8-1.1.0.tgz",
|
||||
"integrity": "sha512-Vvn3zZrhQZkkBE8LSuW3em98c0FwgO4nxzv6OdSxPKJIEKY2bGbHn+mhGIPerzI4twdxaP8/0+06HBpwf345Lw==",
|
||||
"version": "1.1.1",
|
||||
"resolved": "https://registry.npmjs.org/@protobufjs/utf8/-/utf8-1.1.1.tgz",
|
||||
"integrity": "sha512-oOAWABowe8EAbMyWKM0tYDKi8Yaox52D+HWZhAIJqQXbqe0xI/GV7FhLWqlEKreMkfDjshR5FKgi3mnle0h6Eg==",
|
||||
"license": "BSD-3-Clause"
|
||||
},
|
||||
"node_modules/@types/node": {
|
||||
"version": "25.2.3",
|
||||
"resolved": "https://registry.npmjs.org/@types/node/-/node-25.2.3.tgz",
|
||||
"integrity": "sha512-m0jEgYlYz+mDJZ2+F4v8D1AyQb+QzsNqRuI7xg1VQX/KlKS0qT9r1Mo16yo5F/MtifXFgaofIFsdFMox2SxIbQ==",
|
||||
"version": "25.6.2",
|
||||
"resolved": "https://registry.npmjs.org/@types/node/-/node-25.6.2.tgz",
|
||||
"integrity": "sha512-sokuT28dxf9JT5Kady1fsXOvI4HVpjZa95NKT5y9PNTIrs2AsobR4GFAA90ZG8M+nxVRLysCXsVj6eGC7Vbrlw==",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"undici-types": "~7.16.0"
|
||||
"undici-types": "~7.19.0"
|
||||
}
|
||||
},
|
||||
"node_modules/@types/retry": {
|
||||
"version": "0.12.0",
|
||||
"resolved": "https://registry.npmjs.org/@types/retry/-/retry-0.12.0.tgz",
|
||||
"integrity": "sha512-wWKOClTTiizcZhXnPY4wikVAwmdYHp8q6DmC+EJUzAMsycb7HB32Kh9RN4+0gExjmPmZSAQjgURXIGATPegAvA==",
|
||||
"license": "MIT"
|
||||
},
|
||||
"node_modules/accepts": {
|
||||
"version": "1.3.8",
|
||||
"resolved": "https://registry.npmjs.org/accepts/-/accepts-1.3.8.tgz",
|
||||
@@ -158,42 +172,12 @@
|
||||
"node": ">= 14"
|
||||
}
|
||||
},
|
||||
"node_modules/ansi-regex": {
|
||||
"version": "6.2.2",
|
||||
"resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-6.2.2.tgz",
|
||||
"integrity": "sha512-Bq3SmSpyFHaWjPk8If9yc6svM8c56dB5BAtW4Qbw5jHTwwXXcTLoRMkpDJp6VL0XzlWaCHTXrkFURMYmD0sLqg==",
|
||||
"license": "MIT",
|
||||
"engines": {
|
||||
"node": ">=12"
|
||||
},
|
||||
"funding": {
|
||||
"url": "https://github.com/chalk/ansi-regex?sponsor=1"
|
||||
}
|
||||
},
|
||||
"node_modules/ansi-styles": {
|
||||
"version": "6.2.3",
|
||||
"resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-6.2.3.tgz",
|
||||
"integrity": "sha512-4Dj6M28JB+oAH8kFkTLUo+a2jwOFkuqb3yucU0CANcRRUbxS0cP0nZYCGjcc3BNXwRIsUVmDGgzawme7zvJHvg==",
|
||||
"license": "MIT",
|
||||
"engines": {
|
||||
"node": ">=12"
|
||||
},
|
||||
"funding": {
|
||||
"url": "https://github.com/chalk/ansi-styles?sponsor=1"
|
||||
}
|
||||
},
|
||||
"node_modules/array-flatten": {
|
||||
"version": "1.1.1",
|
||||
"resolved": "https://registry.npmjs.org/array-flatten/-/array-flatten-1.1.1.tgz",
|
||||
"integrity": "sha512-PCVAQswWemu6UdxsDFFX/+gVeYqKAod3D3UVm91jHwynguOwAvYPhx8nNlM++NqRcK6CxxpUafjmhIdKiHibqg==",
|
||||
"license": "MIT"
|
||||
},
|
||||
"node_modules/balanced-match": {
|
||||
"version": "1.0.2",
|
||||
"resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-1.0.2.tgz",
|
||||
"integrity": "sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw==",
|
||||
"license": "MIT"
|
||||
},
|
||||
"node_modules/base64-js": {
|
||||
"version": "1.5.1",
|
||||
"resolved": "https://registry.npmjs.org/base64-js/-/base64-js-1.5.1.tgz",
|
||||
@@ -224,9 +208,9 @@
|
||||
}
|
||||
},
|
||||
"node_modules/body-parser": {
|
||||
"version": "1.20.4",
|
||||
"resolved": "https://registry.npmjs.org/body-parser/-/body-parser-1.20.4.tgz",
|
||||
"integrity": "sha512-ZTgYYLMOXY9qKU/57FAo8F+HA2dGX7bqGc71txDRC1rS4frdFI5R7NhluHxH6M0YItAP0sHB4uqAOcYKxO6uGA==",
|
||||
"version": "1.20.5",
|
||||
"resolved": "https://registry.npmjs.org/body-parser/-/body-parser-1.20.5.tgz",
|
||||
"integrity": "sha512-3grm+/2tUOvu2cjJkvsIxrv/wVpfXQW4PsQHYm7yk4vfpu7Ekl6nEsYBoJUL6qDwZUx8wUhQ8tR2qz+ad9c9OA==",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"bytes": "~3.1.2",
|
||||
@@ -237,7 +221,7 @@
|
||||
"http-errors": "~2.0.1",
|
||||
"iconv-lite": "~0.4.24",
|
||||
"on-finished": "~2.4.1",
|
||||
"qs": "~6.14.0",
|
||||
"qs": "~6.15.1",
|
||||
"raw-body": "~2.5.3",
|
||||
"type-is": "~1.6.18",
|
||||
"unpipe": "~1.0.0"
|
||||
@@ -247,13 +231,19 @@
|
||||
"npm": "1.2.8000 || >= 1.4.16"
|
||||
}
|
||||
},
|
||||
"node_modules/brace-expansion": {
|
||||
"version": "2.0.2",
|
||||
"resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-2.0.2.tgz",
|
||||
"integrity": "sha512-Jt0vHyM+jmUBqojB7E1NIYadt0vI0Qxjxd2TErW94wDz+E2LAm5vKMXXwg6ZZBTHPuUlDgQHKXvjGBdfcF1ZDQ==",
|
||||
"license": "MIT",
|
||||
"node_modules/body-parser/node_modules/qs": {
|
||||
"version": "6.15.1",
|
||||
"resolved": "https://registry.npmjs.org/qs/-/qs-6.15.1.tgz",
|
||||
"integrity": "sha512-6YHEFRL9mfgcAvql/XhwTvf5jKcOiiupt2FiJxHkiX1z4j7WL8J/jRHYLluORvc1XxB5rV20KoeK00gVJamspg==",
|
||||
"license": "BSD-3-Clause",
|
||||
"dependencies": {
|
||||
"balanced-match": "^1.0.0"
|
||||
"side-channel": "^1.1.0"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">=0.6"
|
||||
},
|
||||
"funding": {
|
||||
"url": "https://github.com/sponsors/ljharb"
|
||||
}
|
||||
},
|
||||
"node_modules/buffer-equal-constant-time": {
|
||||
@@ -300,24 +290,6 @@
|
||||
"url": "https://github.com/sponsors/ljharb"
|
||||
}
|
||||
},
|
||||
"node_modules/color-convert": {
|
||||
"version": "2.0.1",
|
||||
"resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz",
|
||||
"integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"color-name": "~1.1.4"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">=7.0.0"
|
||||
}
|
||||
},
|
||||
"node_modules/color-name": {
|
||||
"version": "1.1.4",
|
||||
"resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz",
|
||||
"integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==",
|
||||
"license": "MIT"
|
||||
},
|
||||
"node_modules/content-disposition": {
|
||||
"version": "0.5.4",
|
||||
"resolved": "https://registry.npmjs.org/content-disposition/-/content-disposition-0.5.4.tgz",
|
||||
@@ -371,20 +343,6 @@
|
||||
"url": "https://opencollective.com/express"
|
||||
}
|
||||
},
|
||||
"node_modules/cross-spawn": {
|
||||
"version": "7.0.6",
|
||||
"resolved": "https://registry.npmjs.org/cross-spawn/-/cross-spawn-7.0.6.tgz",
|
||||
"integrity": "sha512-uV2QOWP2nWzsy2aMp8aRibhi9dlzF5Hgh5SHaB9OiTGEyDTiJJyx0uy51QXdyWbtAHNua4XJzUKca3OzKUd3vA==",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"path-key": "^3.1.0",
|
||||
"shebang-command": "^2.0.0",
|
||||
"which": "^2.0.1"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">= 8"
|
||||
}
|
||||
},
|
||||
"node_modules/data-uri-to-buffer": {
|
||||
"version": "4.0.1",
|
||||
"resolved": "https://registry.npmjs.org/data-uri-to-buffer/-/data-uri-to-buffer-4.0.1.tgz",
|
||||
@@ -436,12 +394,6 @@
|
||||
"node": ">= 0.4"
|
||||
}
|
||||
},
|
||||
"node_modules/eastasianwidth": {
|
||||
"version": "0.2.0",
|
||||
"resolved": "https://registry.npmjs.org/eastasianwidth/-/eastasianwidth-0.2.0.tgz",
|
||||
"integrity": "sha512-I88TYZWc9XiYHRQ4/3c5rjjfgkjhLyW2luGIheGERbNQ6OY7yTybanSpDXZa8y7VUP9YmDcYa+eyq4ca7iLqWA==",
|
||||
"license": "MIT"
|
||||
},
|
||||
"node_modules/ecdsa-sig-formatter": {
|
||||
"version": "1.0.11",
|
||||
"resolved": "https://registry.npmjs.org/ecdsa-sig-formatter/-/ecdsa-sig-formatter-1.0.11.tgz",
|
||||
@@ -457,12 +409,6 @@
|
||||
"integrity": "sha512-WMwm9LhRUo+WUaRN+vRuETqG89IgZphVSNkdFgeb6sS/E4OrDIN7t48CAewSHXc6C8lefD8KKfr5vY61brQlow==",
|
||||
"license": "MIT"
|
||||
},
|
||||
"node_modules/emoji-regex": {
|
||||
"version": "9.2.2",
|
||||
"resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-9.2.2.tgz",
|
||||
"integrity": "sha512-L18DaJsXSUk2+42pv8mLs5jJT2hqFkFE4j21wOmgbUqsZ2hL72NsUU785g9RXgo3s0ZNgVl42TiHp3ZtOv/Vyg==",
|
||||
"license": "MIT"
|
||||
},
|
||||
"node_modules/encodeurl": {
|
||||
"version": "2.0.0",
|
||||
"resolved": "https://registry.npmjs.org/encodeurl/-/encodeurl-2.0.0.tgz",
|
||||
@@ -610,22 +556,6 @@
|
||||
"node": ">= 0.8"
|
||||
}
|
||||
},
|
||||
"node_modules/foreground-child": {
|
||||
"version": "3.3.1",
|
||||
"resolved": "https://registry.npmjs.org/foreground-child/-/foreground-child-3.3.1.tgz",
|
||||
"integrity": "sha512-gIXjKqtFuWEgzFRJA9WCQeSJLZDjgJUOMCMzxtvFq/37KojM1BFGufqsCy0r4qSQmYLsZYMeyRqzIWOMup03sw==",
|
||||
"license": "ISC",
|
||||
"dependencies": {
|
||||
"cross-spawn": "^7.0.6",
|
||||
"signal-exit": "^4.0.1"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">=14"
|
||||
},
|
||||
"funding": {
|
||||
"url": "https://github.com/sponsors/isaacs"
|
||||
}
|
||||
},
|
||||
"node_modules/formdata-polyfill": {
|
||||
"version": "4.0.10",
|
||||
"resolved": "https://registry.npmjs.org/formdata-polyfill/-/formdata-polyfill-4.0.10.tgz",
|
||||
@@ -666,15 +596,14 @@
|
||||
}
|
||||
},
|
||||
"node_modules/gaxios": {
|
||||
"version": "7.1.3",
|
||||
"resolved": "https://registry.npmjs.org/gaxios/-/gaxios-7.1.3.tgz",
|
||||
"integrity": "sha512-YGGyuEdVIjqxkxVH1pUTMY/XtmmsApXrCVv5EU25iX6inEPbV+VakJfLealkBtJN69AQmh1eGOdCl9Sm1UP6XQ==",
|
||||
"version": "7.1.4",
|
||||
"resolved": "https://registry.npmjs.org/gaxios/-/gaxios-7.1.4.tgz",
|
||||
"integrity": "sha512-bTIgTsM2bWn3XklZISBTQX7ZSddGW+IO3bMdGaemHZ3tbqExMENHLx6kKZ/KlejgrMtj8q7wBItt51yegqalrA==",
|
||||
"license": "Apache-2.0",
|
||||
"dependencies": {
|
||||
"extend": "^3.0.2",
|
||||
"https-proxy-agent": "^7.0.1",
|
||||
"node-fetch": "^3.3.2",
|
||||
"rimraf": "^5.0.1"
|
||||
"node-fetch": "^3.3.2"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">=18"
|
||||
@@ -731,39 +660,17 @@
|
||||
"node": ">= 0.4"
|
||||
}
|
||||
},
|
||||
"node_modules/glob": {
|
||||
"version": "10.5.0",
|
||||
"resolved": "https://registry.npmjs.org/glob/-/glob-10.5.0.tgz",
|
||||
"integrity": "sha512-DfXN8DfhJ7NH3Oe7cFmu3NCu1wKbkReJ8TorzSAFbSKrlNaQSKfIzqYqVY8zlbs2NLBbWpRiU52GX2PbaBVNkg==",
|
||||
"deprecated": "Old versions of glob are not supported, and contain widely publicized security vulnerabilities, which have been fixed in the current version. Please update. Support for old versions may be purchased (at exorbitant rates) by contacting i@izs.me",
|
||||
"license": "ISC",
|
||||
"dependencies": {
|
||||
"foreground-child": "^3.1.0",
|
||||
"jackspeak": "^3.1.2",
|
||||
"minimatch": "^9.0.4",
|
||||
"minipass": "^7.1.2",
|
||||
"package-json-from-dist": "^1.0.0",
|
||||
"path-scurry": "^1.11.1"
|
||||
},
|
||||
"bin": {
|
||||
"glob": "dist/esm/bin.mjs"
|
||||
},
|
||||
"funding": {
|
||||
"url": "https://github.com/sponsors/isaacs"
|
||||
}
|
||||
},
|
||||
"node_modules/google-auth-library": {
|
||||
"version": "10.5.0",
|
||||
"resolved": "https://registry.npmjs.org/google-auth-library/-/google-auth-library-10.5.0.tgz",
|
||||
"integrity": "sha512-7ABviyMOlX5hIVD60YOfHw4/CxOfBhyduaYB+wbFWCWoni4N7SLcV46hrVRktuBbZjFC9ONyqamZITN7q3n32w==",
|
||||
"version": "10.6.2",
|
||||
"resolved": "https://registry.npmjs.org/google-auth-library/-/google-auth-library-10.6.2.tgz",
|
||||
"integrity": "sha512-e27Z6EThmVNNvtYASwQxose/G57rkRuaRbQyxM2bvYLLX/GqWZ5chWq2EBoUchJbCc57eC9ArzO5wMsEmWftCw==",
|
||||
"license": "Apache-2.0",
|
||||
"dependencies": {
|
||||
"base64-js": "^1.3.0",
|
||||
"ecdsa-sig-formatter": "^1.0.11",
|
||||
"gaxios": "^7.0.0",
|
||||
"gcp-metadata": "^8.0.0",
|
||||
"google-logging-utils": "^1.0.0",
|
||||
"gtoken": "^8.0.0",
|
||||
"gaxios": "^7.1.4",
|
||||
"gcp-metadata": "8.1.2",
|
||||
"google-logging-utils": "1.1.3",
|
||||
"jws": "^4.0.0"
|
||||
},
|
||||
"engines": {
|
||||
@@ -791,19 +698,6 @@
|
||||
"url": "https://github.com/sponsors/ljharb"
|
||||
}
|
||||
},
|
||||
"node_modules/gtoken": {
|
||||
"version": "8.0.0",
|
||||
"resolved": "https://registry.npmjs.org/gtoken/-/gtoken-8.0.0.tgz",
|
||||
"integrity": "sha512-+CqsMbHPiSTdtSO14O51eMNlrp9N79gmeqmXeouJOhfucAedHw9noVe/n5uJk3tbKE6a+6ZCQg3RPhVhHByAIw==",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"gaxios": "^7.0.0",
|
||||
"jws": "^4.0.0"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">=18"
|
||||
}
|
||||
},
|
||||
"node_modules/has-symbols": {
|
||||
"version": "1.1.0",
|
||||
"resolved": "https://registry.npmjs.org/has-symbols/-/has-symbols-1.1.0.tgz",
|
||||
@@ -817,9 +711,9 @@
|
||||
}
|
||||
},
|
||||
"node_modules/hasown": {
|
||||
"version": "2.0.2",
|
||||
"resolved": "https://registry.npmjs.org/hasown/-/hasown-2.0.2.tgz",
|
||||
"integrity": "sha512-0hJU9SCPvmMzIBdZFqNPXWa6dqh7WdH0cII9y+CyS8rG3nL48Bclra9HmKhVVUHyPWNH5Y7xDwAB7bfgSjkUMQ==",
|
||||
"version": "2.0.3",
|
||||
"resolved": "https://registry.npmjs.org/hasown/-/hasown-2.0.3.tgz",
|
||||
"integrity": "sha512-ej4AhfhfL2Q2zpMmLo7U1Uv9+PyhIZpgQLGT1F9miIGmiCJIoCgSmczFdrc97mWT4kVY72KA+WnnhJ5pghSvSg==",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"function-bind": "^1.1.2"
|
||||
@@ -911,48 +805,6 @@
|
||||
"node": ">= 0.10"
|
||||
}
|
||||
},
|
||||
"node_modules/is-fullwidth-code-point": {
|
||||
"version": "3.0.0",
|
||||
"resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-3.0.0.tgz",
|
||||
"integrity": "sha512-zymm5+u+sCsSWyD9qNaejV3DFvhCKclKdizYaJUuHA83RLjb7nSuGnddCHGv0hk+KY7BMAlsWeK4Ueg6EV6XQg==",
|
||||
"license": "MIT",
|
||||
"engines": {
|
||||
"node": ">=8"
|
||||
}
|
||||
},
|
||||
"node_modules/is-network-error": {
|
||||
"version": "1.3.0",
|
||||
"resolved": "https://registry.npmjs.org/is-network-error/-/is-network-error-1.3.0.tgz",
|
||||
"integrity": "sha512-6oIwpsgRfnDiyEDLMay/GqCl3HoAtH5+RUKW29gYkL0QA+ipzpDLA16yQs7/RHCSu+BwgbJaOUqa4A99qNVQVw==",
|
||||
"license": "MIT",
|
||||
"engines": {
|
||||
"node": ">=16"
|
||||
},
|
||||
"funding": {
|
||||
"url": "https://github.com/sponsors/sindresorhus"
|
||||
}
|
||||
},
|
||||
"node_modules/isexe": {
|
||||
"version": "2.0.0",
|
||||
"resolved": "https://registry.npmjs.org/isexe/-/isexe-2.0.0.tgz",
|
||||
"integrity": "sha512-RHxMLp9lnKHGHRng9QFhRCMbYAcVpn69smSGcq3f36xjgVVWThj4qqLbTLlq7Ssj8B+fIQ1EuCEGI2lKsyQeIw==",
|
||||
"license": "ISC"
|
||||
},
|
||||
"node_modules/jackspeak": {
|
||||
"version": "3.4.3",
|
||||
"resolved": "https://registry.npmjs.org/jackspeak/-/jackspeak-3.4.3.tgz",
|
||||
"integrity": "sha512-OGlZQpz2yfahA/Rd1Y8Cd9SIEsqvXkLVoSw/cgwhnhFMDbsQFeZYoJJ7bIZBS9BcamUW96asq/npPWugM+RQBw==",
|
||||
"license": "BlueOak-1.0.0",
|
||||
"dependencies": {
|
||||
"@isaacs/cliui": "^8.0.2"
|
||||
},
|
||||
"funding": {
|
||||
"url": "https://github.com/sponsors/isaacs"
|
||||
},
|
||||
"optionalDependencies": {
|
||||
"@pkgjs/parseargs": "^0.11.0"
|
||||
}
|
||||
},
|
||||
"node_modules/json-bigint": {
|
||||
"version": "1.0.0",
|
||||
"resolved": "https://registry.npmjs.org/json-bigint/-/json-bigint-1.0.0.tgz",
|
||||
@@ -989,12 +841,6 @@
|
||||
"integrity": "sha512-mNAgZ1GmyNhD7AuqnTG3/VQ26o760+ZYBPKjPvugO8+nLbYfX6TVpJPseBvopbdY+qpZ/lKUnmEc1LeZYS3QAA==",
|
||||
"license": "Apache-2.0"
|
||||
},
|
||||
"node_modules/lru-cache": {
|
||||
"version": "10.4.3",
|
||||
"resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-10.4.3.tgz",
|
||||
"integrity": "sha512-JNAzZcXrCt42VGLuYz0zfAzDfAvJWW6AfYlDBQyDV5DClI2m5sAmK+OIO7s59XfsRsWHp02jAJrRadPRGTt6SQ==",
|
||||
"license": "ISC"
|
||||
},
|
||||
"node_modules/math-intrinsics": {
|
||||
"version": "1.1.0",
|
||||
"resolved": "https://registry.npmjs.org/math-intrinsics/-/math-intrinsics-1.1.0.tgz",
|
||||
@@ -1064,30 +910,6 @@
|
||||
"node": ">= 0.6"
|
||||
}
|
||||
},
|
||||
"node_modules/minimatch": {
|
||||
"version": "9.0.5",
|
||||
"resolved": "https://registry.npmjs.org/minimatch/-/minimatch-9.0.5.tgz",
|
||||
"integrity": "sha512-G6T0ZX48xgozx7587koeX9Ys2NYy6Gmv//P89sEte9V9whIapMNF4idKxnW2QtCcLiTWlb/wfCabAtAFWhhBow==",
|
||||
"license": "ISC",
|
||||
"dependencies": {
|
||||
"brace-expansion": "^2.0.1"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">=16 || 14 >=14.17"
|
||||
},
|
||||
"funding": {
|
||||
"url": "https://github.com/sponsors/isaacs"
|
||||
}
|
||||
},
|
||||
"node_modules/minipass": {
|
||||
"version": "7.1.2",
|
||||
"resolved": "https://registry.npmjs.org/minipass/-/minipass-7.1.2.tgz",
|
||||
"integrity": "sha512-qOOzS1cBTWYF4BH8fVePDBOO9iptMnGUEZwNc/cMWnTV2nVLZ7VoNWEPHkYczZA0pdoA7dl6e7FL659nX9S2aw==",
|
||||
"license": "ISC",
|
||||
"engines": {
|
||||
"node": ">=16 || 14 >=14.17"
|
||||
}
|
||||
},
|
||||
"node_modules/ms": {
|
||||
"version": "2.0.0",
|
||||
"resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz",
|
||||
@@ -1175,26 +997,18 @@
|
||||
}
|
||||
},
|
||||
"node_modules/p-retry": {
|
||||
"version": "7.1.1",
|
||||
"resolved": "https://registry.npmjs.org/p-retry/-/p-retry-7.1.1.tgz",
|
||||
"integrity": "sha512-J5ApzjyRkkf601HpEeykoiCvzHQjWxPAHhyjFcEUP2SWq0+35NKh8TLhpLw+Dkq5TZBFvUM6UigdE9hIVYTl5w==",
|
||||
"version": "4.6.2",
|
||||
"resolved": "https://registry.npmjs.org/p-retry/-/p-retry-4.6.2.tgz",
|
||||
"integrity": "sha512-312Id396EbJdvRONlngUx0NydfrIQ5lsYu0znKVUzVvArzEIt08V1qhtyESbGVd1FGX7UKtiFp5uwKZdM8wIuQ==",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"is-network-error": "^1.1.0"
|
||||
"@types/retry": "0.12.0",
|
||||
"retry": "^0.13.1"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">=20"
|
||||
},
|
||||
"funding": {
|
||||
"url": "https://github.com/sponsors/sindresorhus"
|
||||
"node": ">=8"
|
||||
}
|
||||
},
|
||||
"node_modules/package-json-from-dist": {
|
||||
"version": "1.0.1",
|
||||
"resolved": "https://registry.npmjs.org/package-json-from-dist/-/package-json-from-dist-1.0.1.tgz",
|
||||
"integrity": "sha512-UEZIS3/by4OC8vL3P2dTXRETpebLI2NiI5vIrjaD/5UtrkFX/tNbwjTSRAGC/+7CAo2pIcBaRgWmcBBHcsaCIw==",
|
||||
"license": "BlueOak-1.0.0"
|
||||
},
|
||||
"node_modules/parseurl": {
|
||||
"version": "1.3.3",
|
||||
"resolved": "https://registry.npmjs.org/parseurl/-/parseurl-1.3.3.tgz",
|
||||
@@ -1204,54 +1018,29 @@
|
||||
"node": ">= 0.8"
|
||||
}
|
||||
},
|
||||
"node_modules/path-key": {
|
||||
"version": "3.1.1",
|
||||
"resolved": "https://registry.npmjs.org/path-key/-/path-key-3.1.1.tgz",
|
||||
"integrity": "sha512-ojmeN0qd+y0jszEtoY48r0Peq5dwMEkIlCOu6Q5f41lfkswXuKtYrhgoTpLnyIcHm24Uhqx+5Tqm2InSwLhE6Q==",
|
||||
"license": "MIT",
|
||||
"engines": {
|
||||
"node": ">=8"
|
||||
}
|
||||
},
|
||||
"node_modules/path-scurry": {
|
||||
"version": "1.11.1",
|
||||
"resolved": "https://registry.npmjs.org/path-scurry/-/path-scurry-1.11.1.tgz",
|
||||
"integrity": "sha512-Xa4Nw17FS9ApQFJ9umLiJS4orGjm7ZzwUrwamcGQuHSzDyth9boKDaycYdDcZDuqYATXw4HFXgaqWTctW/v1HA==",
|
||||
"license": "BlueOak-1.0.0",
|
||||
"dependencies": {
|
||||
"lru-cache": "^10.2.0",
|
||||
"minipass": "^5.0.0 || ^6.0.2 || ^7.0.0"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">=16 || 14 >=14.18"
|
||||
},
|
||||
"funding": {
|
||||
"url": "https://github.com/sponsors/isaacs"
|
||||
}
|
||||
},
|
||||
"node_modules/path-to-regexp": {
|
||||
"version": "0.1.12",
|
||||
"resolved": "https://registry.npmjs.org/path-to-regexp/-/path-to-regexp-0.1.12.tgz",
|
||||
"integrity": "sha512-RA1GjUVMnvYFxuqovrEqZoxxW5NUZqbwKtYz/Tt7nXerk0LbLblQmrsgdeOxV5SFHf0UDggjS/bSeOZwt1pmEQ==",
|
||||
"version": "0.1.13",
|
||||
"resolved": "https://registry.npmjs.org/path-to-regexp/-/path-to-regexp-0.1.13.tgz",
|
||||
"integrity": "sha512-A/AGNMFN3c8bOlvV9RreMdrv7jsmF9XIfDeCd87+I8RNg6s78BhJxMu69NEMHBSJFxKidViTEdruRwEk/WIKqA==",
|
||||
"license": "MIT"
|
||||
},
|
||||
"node_modules/protobufjs": {
|
||||
"version": "7.5.4",
|
||||
"resolved": "https://registry.npmjs.org/protobufjs/-/protobufjs-7.5.4.tgz",
|
||||
"integrity": "sha512-CvexbZtbov6jW2eXAvLukXjXUW1TzFaivC46BpWc/3BpcCysb5Vffu+B3XHMm8lVEuy2Mm4XGex8hBSg1yapPg==",
|
||||
"version": "7.5.6",
|
||||
"resolved": "https://registry.npmjs.org/protobufjs/-/protobufjs-7.5.6.tgz",
|
||||
"integrity": "sha512-M71sTMB146U3u0di3yup8iM+zv8yPRNQVr1KK4tyBitl3qFvEGucq/rGDRShD2rsJhtN02RJaJ7j5X5hmy8SJg==",
|
||||
"hasInstallScript": true,
|
||||
"license": "BSD-3-Clause",
|
||||
"dependencies": {
|
||||
"@protobufjs/aspromise": "^1.1.2",
|
||||
"@protobufjs/base64": "^1.1.2",
|
||||
"@protobufjs/codegen": "^2.0.4",
|
||||
"@protobufjs/codegen": "^2.0.5",
|
||||
"@protobufjs/eventemitter": "^1.1.0",
|
||||
"@protobufjs/fetch": "^1.1.0",
|
||||
"@protobufjs/float": "^1.0.2",
|
||||
"@protobufjs/inquire": "^1.1.0",
|
||||
"@protobufjs/inquire": "^1.1.1",
|
||||
"@protobufjs/path": "^1.1.2",
|
||||
"@protobufjs/pool": "^1.1.0",
|
||||
"@protobufjs/utf8": "^1.1.0",
|
||||
"@protobufjs/utf8": "^1.1.1",
|
||||
"@types/node": ">=13.7.0",
|
||||
"long": "^5.0.0"
|
||||
},
|
||||
@@ -1311,19 +1100,13 @@
|
||||
"node": ">= 0.8"
|
||||
}
|
||||
},
|
||||
"node_modules/rimraf": {
|
||||
"version": "5.0.10",
|
||||
"resolved": "https://registry.npmjs.org/rimraf/-/rimraf-5.0.10.tgz",
|
||||
"integrity": "sha512-l0OE8wL34P4nJH/H2ffoaniAokM2qSmrtXHmlpvYr5AVVX8msAyW0l8NVJFDxlSK4u3Uh/f41cQheDVdnYijwQ==",
|
||||
"license": "ISC",
|
||||
"dependencies": {
|
||||
"glob": "^10.3.7"
|
||||
},
|
||||
"bin": {
|
||||
"rimraf": "dist/esm/bin.mjs"
|
||||
},
|
||||
"funding": {
|
||||
"url": "https://github.com/sponsors/isaacs"
|
||||
"node_modules/retry": {
|
||||
"version": "0.13.1",
|
||||
"resolved": "https://registry.npmjs.org/retry/-/retry-0.13.1.tgz",
|
||||
"integrity": "sha512-XQBQ3I8W1Cge0Seh+6gjj03LbmRFWuoszgK9ooCpwYIrhhoO80pfq4cUkU5DkknwfOfFteRwlZ56PYOGYyFWdg==",
|
||||
"license": "MIT",
|
||||
"engines": {
|
||||
"node": ">= 4"
|
||||
}
|
||||
},
|
||||
"node_modules/safe-buffer": {
|
||||
@@ -1403,27 +1186,6 @@
|
||||
"integrity": "sha512-E5LDX7Wrp85Kil5bhZv46j8jOeboKq5JMmYM3gVGdGH8xFpPWXUMsNrlODCrkoxMEeNi/XZIwuRvY4XNwYMJpw==",
|
||||
"license": "ISC"
|
||||
},
|
||||
"node_modules/shebang-command": {
|
||||
"version": "2.0.0",
|
||||
"resolved": "https://registry.npmjs.org/shebang-command/-/shebang-command-2.0.0.tgz",
|
||||
"integrity": "sha512-kHxr2zZpYtdmrN1qDjrrX/Z1rR1kG8Dx+gkpK1G4eXmvXswmcE1hTWBWYUzlraYw1/yZp6YuDY77YtvbN0dmDA==",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"shebang-regex": "^3.0.0"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">=8"
|
||||
}
|
||||
},
|
||||
"node_modules/shebang-regex": {
|
||||
"version": "3.0.0",
|
||||
"resolved": "https://registry.npmjs.org/shebang-regex/-/shebang-regex-3.0.0.tgz",
|
||||
"integrity": "sha512-7++dFhtcx3353uBaq8DDR4NuxBetBzC7ZQOhmTQInHEd6bSrXdiEyzCvG07Z44UYdLShWUyXt5M/yhz8ekcb1A==",
|
||||
"license": "MIT",
|
||||
"engines": {
|
||||
"node": ">=8"
|
||||
}
|
||||
},
|
||||
"node_modules/side-channel": {
|
||||
"version": "1.1.0",
|
||||
"resolved": "https://registry.npmjs.org/side-channel/-/side-channel-1.1.0.tgz",
|
||||
@@ -1444,13 +1206,13 @@
|
||||
}
|
||||
},
|
||||
"node_modules/side-channel-list": {
|
||||
"version": "1.0.0",
|
||||
"resolved": "https://registry.npmjs.org/side-channel-list/-/side-channel-list-1.0.0.tgz",
|
||||
"integrity": "sha512-FCLHtRD/gnpCiCHEiJLOwdmFP+wzCmDEkc9y7NsYxeF4u7Btsn1ZuwgwJGxImImHicJArLP4R0yX4c2KCrMrTA==",
|
||||
"version": "1.0.1",
|
||||
"resolved": "https://registry.npmjs.org/side-channel-list/-/side-channel-list-1.0.1.tgz",
|
||||
"integrity": "sha512-mjn/0bi/oUURjc5Xl7IaWi/OJJJumuoJFQJfDDyO46+hBWsfaVM65TBHq2eoZBhzl9EchxOijpkbRC8SVBQU0w==",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"es-errors": "^1.3.0",
|
||||
"object-inspect": "^1.13.3"
|
||||
"object-inspect": "^1.13.4"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">= 0.4"
|
||||
@@ -1496,18 +1258,6 @@
|
||||
"url": "https://github.com/sponsors/ljharb"
|
||||
}
|
||||
},
|
||||
"node_modules/signal-exit": {
|
||||
"version": "4.1.0",
|
||||
"resolved": "https://registry.npmjs.org/signal-exit/-/signal-exit-4.1.0.tgz",
|
||||
"integrity": "sha512-bzyZ1e88w9O1iNJbKnOlvYTrWPDl46O1bG0D3XInv+9tkPrxrN8jUUTiFlDkkmKWgn1M6CfIA13SuGqOa9Korw==",
|
||||
"license": "ISC",
|
||||
"engines": {
|
||||
"node": ">=14"
|
||||
},
|
||||
"funding": {
|
||||
"url": "https://github.com/sponsors/isaacs"
|
||||
}
|
||||
},
|
||||
"node_modules/statuses": {
|
||||
"version": "2.0.2",
|
||||
"resolved": "https://registry.npmjs.org/statuses/-/statuses-2.0.2.tgz",
|
||||
@@ -1517,102 +1267,6 @@
|
||||
"node": ">= 0.8"
|
||||
}
|
||||
},
|
||||
"node_modules/string-width": {
|
||||
"version": "5.1.2",
|
||||
"resolved": "https://registry.npmjs.org/string-width/-/string-width-5.1.2.tgz",
|
||||
"integrity": "sha512-HnLOCR3vjcY8beoNLtcjZ5/nxn2afmME6lhrDrebokqMap+XbeW8n9TXpPDOqdGK5qcI3oT0GKTW6wC7EMiVqA==",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"eastasianwidth": "^0.2.0",
|
||||
"emoji-regex": "^9.2.2",
|
||||
"strip-ansi": "^7.0.1"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">=12"
|
||||
},
|
||||
"funding": {
|
||||
"url": "https://github.com/sponsors/sindresorhus"
|
||||
}
|
||||
},
|
||||
"node_modules/string-width-cjs": {
|
||||
"name": "string-width",
|
||||
"version": "4.2.3",
|
||||
"resolved": "https://registry.npmjs.org/string-width/-/string-width-4.2.3.tgz",
|
||||
"integrity": "sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g==",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"emoji-regex": "^8.0.0",
|
||||
"is-fullwidth-code-point": "^3.0.0",
|
||||
"strip-ansi": "^6.0.1"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">=8"
|
||||
}
|
||||
},
|
||||
"node_modules/string-width-cjs/node_modules/ansi-regex": {
|
||||
"version": "5.0.1",
|
||||
"resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-5.0.1.tgz",
|
||||
"integrity": "sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ==",
|
||||
"license": "MIT",
|
||||
"engines": {
|
||||
"node": ">=8"
|
||||
}
|
||||
},
|
||||
"node_modules/string-width-cjs/node_modules/emoji-regex": {
|
||||
"version": "8.0.0",
|
||||
"resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-8.0.0.tgz",
|
||||
"integrity": "sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A==",
|
||||
"license": "MIT"
|
||||
},
|
||||
"node_modules/string-width-cjs/node_modules/strip-ansi": {
|
||||
"version": "6.0.1",
|
||||
"resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.1.tgz",
|
||||
"integrity": "sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"ansi-regex": "^5.0.1"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">=8"
|
||||
}
|
||||
},
|
||||
"node_modules/strip-ansi": {
|
||||
"version": "7.1.2",
|
||||
"resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-7.1.2.tgz",
|
||||
"integrity": "sha512-gmBGslpoQJtgnMAvOVqGZpEz9dyoKTCzy2nfz/n8aIFhN/jCE/rCmcxabB6jOOHV+0WNnylOxaxBQPSvcWklhA==",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"ansi-regex": "^6.0.1"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">=12"
|
||||
},
|
||||
"funding": {
|
||||
"url": "https://github.com/chalk/strip-ansi?sponsor=1"
|
||||
}
|
||||
},
|
||||
"node_modules/strip-ansi-cjs": {
|
||||
"name": "strip-ansi",
|
||||
"version": "6.0.1",
|
||||
"resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.1.tgz",
|
||||
"integrity": "sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"ansi-regex": "^5.0.1"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">=8"
|
||||
}
|
||||
},
|
||||
"node_modules/strip-ansi-cjs/node_modules/ansi-regex": {
|
||||
"version": "5.0.1",
|
||||
"resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-5.0.1.tgz",
|
||||
"integrity": "sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ==",
|
||||
"license": "MIT",
|
||||
"engines": {
|
||||
"node": ">=8"
|
||||
}
|
||||
},
|
||||
"node_modules/toidentifier": {
|
||||
"version": "1.0.1",
|
||||
"resolved": "https://registry.npmjs.org/toidentifier/-/toidentifier-1.0.1.tgz",
|
||||
@@ -1636,9 +1290,9 @@
|
||||
}
|
||||
},
|
||||
"node_modules/undici-types": {
|
||||
"version": "7.16.0",
|
||||
"resolved": "https://registry.npmjs.org/undici-types/-/undici-types-7.16.0.tgz",
|
||||
"integrity": "sha512-Zz+aZWSj8LE6zoxD+xrjh4VfkIG8Ya6LvYkZqtUQGJPZjYl53ypCaUwWqo7eI0x66KBGeRo+mlBEkMSeSZ38Nw==",
|
||||
"version": "7.19.2",
|
||||
"resolved": "https://registry.npmjs.org/undici-types/-/undici-types-7.19.2.tgz",
|
||||
"integrity": "sha512-qYVnV5OEm2AW8cJMCpdV20CDyaN3g0AjDlOGf1OW4iaDEx8MwdtChUp4zu4H0VP3nDRF/8RKWH+IPp9uW0YGZg==",
|
||||
"license": "MIT"
|
||||
},
|
||||
"node_modules/unpipe": {
|
||||
@@ -1677,116 +1331,10 @@
|
||||
"node": ">= 8"
|
||||
}
|
||||
},
|
||||
"node_modules/which": {
|
||||
"version": "2.0.2",
|
||||
"resolved": "https://registry.npmjs.org/which/-/which-2.0.2.tgz",
|
||||
"integrity": "sha512-BLI3Tl1TW3Pvl70l3yq3Y64i+awpwXqsGBYWkkqMtnbXgrMD+yj7rhW0kuEDxzJaYXGjEW5ogapKNMEKNMjibA==",
|
||||
"license": "ISC",
|
||||
"dependencies": {
|
||||
"isexe": "^2.0.0"
|
||||
},
|
||||
"bin": {
|
||||
"node-which": "bin/node-which"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">= 8"
|
||||
}
|
||||
},
|
||||
"node_modules/wrap-ansi": {
|
||||
"version": "8.1.0",
|
||||
"resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-8.1.0.tgz",
|
||||
"integrity": "sha512-si7QWI6zUMq56bESFvagtmzMdGOtoxfR+Sez11Mobfc7tm+VkUckk9bW2UeffTGVUbOksxmSw0AA2gs8g71NCQ==",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"ansi-styles": "^6.1.0",
|
||||
"string-width": "^5.0.1",
|
||||
"strip-ansi": "^7.0.1"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">=12"
|
||||
},
|
||||
"funding": {
|
||||
"url": "https://github.com/chalk/wrap-ansi?sponsor=1"
|
||||
}
|
||||
},
|
||||
"node_modules/wrap-ansi-cjs": {
|
||||
"name": "wrap-ansi",
|
||||
"version": "7.0.0",
|
||||
"resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-7.0.0.tgz",
|
||||
"integrity": "sha512-YVGIj2kamLSTxw6NsZjoBxfSwsn0ycdesmc4p+Q21c5zPuZ1pl+NfxVdxPtdHvmNVOQ6XSYG4AUtyt/Fi7D16Q==",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"ansi-styles": "^4.0.0",
|
||||
"string-width": "^4.1.0",
|
||||
"strip-ansi": "^6.0.0"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">=10"
|
||||
},
|
||||
"funding": {
|
||||
"url": "https://github.com/chalk/wrap-ansi?sponsor=1"
|
||||
}
|
||||
},
|
||||
"node_modules/wrap-ansi-cjs/node_modules/ansi-regex": {
|
||||
"version": "5.0.1",
|
||||
"resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-5.0.1.tgz",
|
||||
"integrity": "sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ==",
|
||||
"license": "MIT",
|
||||
"engines": {
|
||||
"node": ">=8"
|
||||
}
|
||||
},
|
||||
"node_modules/wrap-ansi-cjs/node_modules/ansi-styles": {
|
||||
"version": "4.3.0",
|
||||
"resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz",
|
||||
"integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"color-convert": "^2.0.1"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">=8"
|
||||
},
|
||||
"funding": {
|
||||
"url": "https://github.com/chalk/ansi-styles?sponsor=1"
|
||||
}
|
||||
},
|
||||
"node_modules/wrap-ansi-cjs/node_modules/emoji-regex": {
|
||||
"version": "8.0.0",
|
||||
"resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-8.0.0.tgz",
|
||||
"integrity": "sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A==",
|
||||
"license": "MIT"
|
||||
},
|
||||
"node_modules/wrap-ansi-cjs/node_modules/string-width": {
|
||||
"version": "4.2.3",
|
||||
"resolved": "https://registry.npmjs.org/string-width/-/string-width-4.2.3.tgz",
|
||||
"integrity": "sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g==",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"emoji-regex": "^8.0.0",
|
||||
"is-fullwidth-code-point": "^3.0.0",
|
||||
"strip-ansi": "^6.0.1"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">=8"
|
||||
}
|
||||
},
|
||||
"node_modules/wrap-ansi-cjs/node_modules/strip-ansi": {
|
||||
"version": "6.0.1",
|
||||
"resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.1.tgz",
|
||||
"integrity": "sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"ansi-regex": "^5.0.1"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">=8"
|
||||
}
|
||||
},
|
||||
"node_modules/ws": {
|
||||
"version": "8.19.0",
|
||||
"resolved": "https://registry.npmjs.org/ws/-/ws-8.19.0.tgz",
|
||||
"integrity": "sha512-blAT2mjOEIi0ZzruJfIhb3nps74PRWTCz1IjglWEEpQl5XS/UNama6u2/rjFkDDouqr4L67ry+1aGIALViWjDg==",
|
||||
"version": "8.20.0",
|
||||
"resolved": "https://registry.npmjs.org/ws/-/ws-8.20.0.tgz",
|
||||
"integrity": "sha512-sAt8BhgNbzCtgGbt2OxmpuryO63ZoDk/sqaB/znQm94T4fCEsy/yV+7CdC1kJhOU9lboAEU7R3kquuycDoibVA==",
|
||||
"license": "MIT",
|
||||
"engines": {
|
||||
"node": ">=10.0.0"
|
||||
|
||||
@@ -8,6 +8,7 @@
|
||||
},
|
||||
"dependencies": {
|
||||
"@google/genai": "^1.41.0",
|
||||
"@keysat/licensing-client": "git+https://github.com/keysat-xyz/keysat-client-ts.git",
|
||||
"cors": "^2.8.5",
|
||||
"express": "^4.21.0"
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user