Download tools/sync_platform.mjs from Wardline/cloak-api: direct link, hf CLI and curl.
- Browser
- Download file 8.17 kB
-
https://huggingface.co/spaces/Wardline/cloak-api/resolve/main/tools/sync_platform.mjs
- Command line
-
hf download hf://spaces/Wardline/cloak-api/tools/sync_platform.mjs
-
curl -L -o sync_platform.mjs https://huggingface.co/spaces/Wardline/cloak-api/resolve/main/tools/sync_platform.mjs
8.17 kB
| // Copies the platform facts the copilot depends on out of a cloakv2 checkout. | |
| // | |
| // node tools/sync_platform.mjs <path-to-cloakv2> (from hf-space/: node tools/sync_platform.mjs ..) | |
| // | |
| // Reads the checkout, never writes to it. Writes copilot/platform/snapshot.json and | |
| // copilot/platform/defaults/<type>.json in this project. Each source file's sha256 is | |
| // recorded, so after a later sync `git diff` shows exactly which platform file moved. | |
| // | |
| // The TypeScript is not compiled: every constant we need is an object or array literal, | |
| // so we cut the literal out by bracket matching, strip `as T` casts and comments, and | |
| // evaluate it. | |
| import { readFileSync, writeFileSync, mkdirSync, existsSync } from 'node:fs'; | |
| import { createHash } from 'node:crypto'; | |
| import { join, dirname } from 'node:path'; | |
| import { fileURLToPath } from 'node:url'; | |
| const HERE = dirname(fileURLToPath(import.meta.url)); | |
| const OUT = join(HERE, '..', 'copilot', 'platform'); | |
| const repo = process.argv[2]; | |
| if (!repo || !existsSync(join(repo, 'server', 'src'))) { | |
| console.error('usage: node tools/sync_platform.mjs <path-to-cloakv2>'); | |
| process.exit(2); | |
| } | |
| const SOURCES = { | |
| presets: 'server/src/modules/org/presets.ts', | |
| blockLists:'server/src/modules/org/blockListPresets.ts', | |
| pii: 'server/src/detection/pii.ts', | |
| patterns: 'server/src/detection/patterns.json', | |
| resolve: 'server/src/modules/pii/resolvePolicy.ts', | |
| ide: 'server/src/modules/ide/policy.ts', | |
| ideCat: 'server/src/modules/ide/catalog.ts', | |
| aiSites: 'dashboard/lib/platformLogos.ts', | |
| rulesUi: 'dashboard/features/policies/DetectionRulesPanel.tsx', | |
| endpoint: 'dashboard/features/agent/PolicyForm.tsx', | |
| ideForm: 'dashboard/features/agent/IdePolicySection.tsx', | |
| email: 'dashboard/features/email/PolicyForm.tsx', | |
| emailT: 'dashboard/features/email/types.ts', | |
| slack: 'dashboard/features/channels/slack/PolicyForm.tsx', | |
| teams: 'dashboard/features/channels/microsoft/teams/PolicyForm.tsx', | |
| chat: 'dashboard/features/channels/google/chat/PolicyForm.tsx', | |
| drive: 'dashboard/features/channels/google/drive/PolicyForm.tsx', | |
| m365files: 'dashboard/features/channels/microsoft/files/PolicyForm.tsx', | |
| s3: 'dashboard/features/channels/aws/s3/PolicyForm.tsx', | |
| gcs: 'dashboard/features/channels/google/gcs/PolicyForm.tsx', | |
| azure: 'dashboard/features/channels/azure/blob/PolicyForm.tsx', | |
| github: 'dashboard/features/channels/github/PolicyForm.tsx', | |
| }; | |
| const text = {}; | |
| const hashes = {}; | |
| for (const [k, rel] of Object.entries(SOURCES)) { | |
| const buf = readFileSync(join(repo, rel)); | |
| text[k] = buf.toString('utf8'); | |
| hashes[rel] = createHash('sha256').update(buf).digest('hex'); | |
| } | |
| // The literal that follows `<name> ... =`, from its opening bracket to the matching close. | |
| function literal(src, name) { | |
| const at = src.search(new RegExp(`\\b(const|let)\\s+${name}\\b[^=]*=`)); | |
| if (at < 0) throw new Error(`${name} not found`); | |
| let i = src.indexOf('=', at) + 1; | |
| while (!'[{'.includes(src[i])) i++; | |
| const start = i; | |
| let depth = 0, quote = null; | |
| for (; i < src.length; i++) { | |
| const c = src[i]; | |
| if (quote) { | |
| if (c === '\\') { i++; continue; } | |
| if (c === quote) quote = null; | |
| continue; | |
| } | |
| if (c === '/' && src[i + 1] === '/') { i = src.indexOf('\n', i); continue; } | |
| if (c === "'" || c === '"' || c === '`') quote = c; | |
| else if (c === '[' || c === '{') depth++; | |
| else if (c === ']' || c === '}') { depth--; if (depth === 0) return src.slice(start, i + 1); } | |
| } | |
| throw new Error(`${name}: unbalanced literal`); | |
| } | |
| function evaluate(lit, scope = {}) { | |
| const js = lit | |
| .replace(/(^|[\s,{[(])\/\/[^\n]*/g, '$1') // line comments, not the // inside URLs | |
| .replace(/\s+as\s+(const|[A-Za-z_][\w]*(\[\])?|'[^']*'(\s*\|\s*'[^']*')*)/g, ''); // casts | |
| return new Function(...Object.keys(scope), `return (${js});`)(...Object.values(scope)); | |
| } | |
| const value = (k, name, scope) => evaluate(literal(text[k], name), scope); | |
| // `new Set([...])` constants: read the array inside. | |
| function setLiteral(k, name) { | |
| const m = text[k].match(new RegExp(`const\\s+${name}\\s*=\\s*new Set\\(\\s*(\\[[\\s\\S]*?\\])\\s*\\)`)); | |
| if (!m) throw new Error(`${name} not found`); | |
| return evaluate(m[1]); | |
| } | |
| const aiSites = value('aiSites', 'AI_PLATFORMS'); | |
| const allHosts = aiSites.map(s => s.host); | |
| const emptyExt = value('endpoint', 'EMPTY_EXT'); | |
| const emptyAgent = value('endpoint', 'EMPTY_AGENT'); | |
| const emptyIdeForm = value('ideForm', 'EMPTY_IDE'); | |
| const ideDefaults = value('ide', 'IDE_DEFAULTS'); | |
| const excludes = value('github', 'DEFAULT_EXCLUDES'); | |
| const channelEmpty = { | |
| email: value('email', 'EMPTY_EMAIL'), | |
| slack: value('slack', 'EMPTY_SLACK'), | |
| teams: value('teams', 'EMPTY_TEAMS'), | |
| chat: value('chat', 'EMPTY_CHAT'), | |
| drive: value('drive', 'EMPTY_DRIVE'), | |
| m365files: value('m365files', 'EMPTY_M365'), | |
| s3: value('s3', 'EMPTY_S3'), | |
| gcs: value('gcs', 'EMPTY_GCS'), | |
| azure: value('azure', 'EMPTY_AZURE'), | |
| github: value('github', 'EMPTY_GH', { DEFAULT_EXCLUDES: excludes }), | |
| }; | |
| // Bodies exactly as each form's handleSubmit builds them on create, before anything is | |
| // edited. The endpoint form spreads the whole form state, so ideControl is sent both at | |
| // the top level (ignored by the server) and inside agentControl (stored). | |
| const defaults = { | |
| endpoint: { | |
| name: '', description: '', groupId: 'default', action: 'warn', | |
| active: true, testMode: false, | |
| enabledEntities: [], allowFileUpload: true, allowCodePaste: true, customSites: [], | |
| extensionControl: emptyExt, | |
| agentControl: { ...emptyAgent, allowedUsbDeviceIds: [], blockedApplications: [], blockedDomains: [], ideControl: emptyIdeForm }, | |
| ideControl: emptyIdeForm, | |
| activeWebCategories: [], exceptions: [], | |
| enabled: true, | |
| enabledSites: allHosts, | |
| channels: {}, | |
| }, | |
| }; | |
| for (const [ch, empty] of Object.entries(channelEmpty)) { | |
| defaults[ch] = { | |
| name: '', description: '', groupId: 'default', active: true, enabled: true, | |
| enabledEntities: [], agentControl: {}, extensionControl: {}, | |
| channels: { [ch]: { ...empty, enabled: true } }, | |
| }; | |
| } | |
| const ideCatalog = { | |
| providers: value('ideCat', 'AI_PROVIDERS').map(p => ({ id: p.id, name: p.name, hosts: p.hosts })), | |
| tools: value('ideCat', 'AI_TOOLS').map(t => ({ key: t.key, name: t.name, kind: t.kind, processes: t.processes, provider: t.provider || null })), | |
| aiConfigFiles: value('ideCat', 'AI_CONFIG_FILES'), | |
| }; | |
| const stamp = { syncedAt: new Date().toISOString(), sources: hashes }; | |
| mkdirSync(join(OUT, 'defaults'), { recursive: true }); | |
| for (const [type, body] of Object.entries(defaults)) { | |
| writeFileSync(join(OUT, 'defaults', `${type}.json`), JSON.stringify({ _source: stamp, body }, null, 2) + '\n'); | |
| } | |
| const snapshot = { | |
| _source: stamp, | |
| aiSites, | |
| genaiPasteSites: value('endpoint', 'GENAI_PRESET_SITES'), | |
| genaiPasteApps: value('endpoint', 'GENAI_PRESET_APPS'), | |
| detectionRules: value('presets', 'DETECTION_RULE_PRESETS'), | |
| blockLists: value('blockLists', 'BLOCK_LIST_PRESETS'), | |
| entityGroupsUi: value('rulesUi', 'ENTITY_GROUPS'), | |
| fileTypes: value('rulesUi', 'FILE_TYPES'), | |
| patternLabels: [...new Set(JSON.parse(text.patterns).patterns.map(p => p.label))].sort(), | |
| highRisk: setLiteral('pii', 'HIGH_RISK'), | |
| mediumRisk: setLiteral('pii', 'MEDIUM_RISK'), | |
| actionRank: value('resolve', 'ACTION_RANK'), | |
| emailActions: value('emailT', 'EMAIL_ACTIONS').map(a => a.value), | |
| ide: { | |
| defaults: ideDefaults, | |
| promptActions: value('ide', 'PROMPT_ACTIONS'), | |
| accessActions: value('ide', 'ACCESS_ACTIONS'), | |
| sensorActions: value('ide', 'SENSOR_ACTIONS'), | |
| riskLevels: value('ide', 'RISK_LEVELS'), | |
| ...ideCatalog, | |
| }, | |
| }; | |
| writeFileSync(join(OUT, 'snapshot.json'), JSON.stringify(snapshot, null, 2) + '\n'); | |
| console.log(`types ${Object.keys(defaults).length}, rules ${snapshot.detectionRules.length}, ` + | |
| `block lists ${snapshot.blockLists.length}, AI sites ${aiSites.length}, ` + | |
| `pattern labels ${snapshot.patternLabels.length}, IDE tools ${ideCatalog.tools.length}, ` + | |
| `IDE providers ${ideCatalog.providers.length}`); | |