@scopebond/hook 0.9.0 → 0.10.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/index.d.ts CHANGED
@@ -15,6 +15,10 @@ export type { Manifest, CapabilityCell, CapabilityState, ProofRecord, HostVarian
15
15
  export { runProofFixtures, deliverProofReceipts, loadProofs, saveProofs, PROOF_FILE } from "./proof.js";
16
16
  export { inspectBudgetExport, loadBudgetExport, localBudgetOf, BUDGET_EXPORT_TYPE } from "./budget-load.js";
17
17
  export { inspectExport, loadPolicyExport, policyScopeDigest, policyBuilds, POLICY_SCOPE_DOMAIN, LOADED_POLICY_FILE } from "./policy-load.js";
18
+ export { inspectManaged, compileManaged, installManaged, restoreLocal, isManaged, readMeta, digestRules, MANAGED_RULE_IDS, MANAGED_DOC_FILE, MANAGED_META_FILE, } from "./managed.js";
19
+ export type { ManagedDocument, ManagedRule, ManagedMeta, RefusalReason } from "./managed.js";
20
+ export { syncPolicy, syncIfDue, SYNC_INTERVAL_MS, INLINE_BUDGET_MS } from "./policy-sync.js";
21
+ export type { SyncOutcome, SyncOptions } from "./policy-sync.js";
18
22
  export { VECTORS, mapVector } from "./vectors.js";
19
23
  export type { Vector, VectorAgent, Dialect } from "./vectors.js";
20
24
  export { scaffold, harnessSnippet, installHarness, placeHook } from "./init.js";
@@ -1 +1 @@
1
- {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAIA,OAAO,EAAE,gBAAgB,EAAE,eAAe,EAAE,cAAc,EAAE,cAAc,EAAE,MAAM,UAAU,CAAC;AAC7F,YAAY,EAAE,MAAM,EAAE,gBAAgB,EAAE,MAAM,UAAU,CAAC;AACzD,OAAO,EAAE,iBAAiB,EAAE,aAAa,EAAE,oBAAoB,EAAE,MAAM,cAAc,CAAC;AACtF,YAAY,EAAE,aAAa,EAAE,QAAQ,EAAE,MAAM,cAAc,CAAC;AAC5D,OAAO,EACL,OAAO,EAAE,YAAY,EAAE,aAAa,EAAE,SAAS,EAAE,SAAS,EAAE,SAAS,EAAE,WAAW,EAAE,UAAU,GAC/F,MAAM,YAAY,CAAC;AACpB,YAAY,EAAE,OAAO,EAAE,QAAQ,EAAE,MAAM,YAAY,CAAC;AACpD,OAAO,EAAE,eAAe,EAAE,oBAAoB,EAAE,iBAAiB,EAAE,MAAM,YAAY,CAAC;AACtF,OAAO,EAAE,cAAc,EAAE,oBAAoB,EAAE,QAAQ,EAAE,MAAM,eAAe,CAAC;AAC/E,YAAY,EAAE,SAAS,EAAE,cAAc,EAAE,MAAM,eAAe,CAAC;AAC/D,OAAO,EAAE,YAAY,EAAE,cAAc,EAAE,MAAM,YAAY,CAAC;AAC1D,YAAY,EAAE,SAAS,EAAE,WAAW,EAAE,MAAM,YAAY,CAAC;AACzD,OAAO,EAAE,aAAa,EAAE,eAAe,EAAE,eAAe,EAAE,kBAAkB,EAAE,uBAAuB,EAAE,sBAAsB,EAAE,MAAM,YAAY,CAAC;AAClJ,OAAO,EAAE,eAAe,EAAE,cAAc,EAAE,SAAS,EAAE,OAAO,EAAE,cAAc,EAAE,YAAY,EAAE,MAAM,mBAAmB,CAAC;AACtH,YAAY,EAAE,QAAQ,EAAE,cAAc,EAAE,eAAe,EAAE,WAAW,EAAE,WAAW,EAAE,UAAU,EAAE,aAAa,EAAE,MAAM,mBAAmB,CAAC;AACxI,OAAO,EAAE,gBAAgB,EAAE,oBAAoB,EAAE,UAAU,EAAE,UAAU,EAAE,UAAU,EAAE,MAAM,YAAY,CAAC;AACxG,OAAO,EAAE,mBAAmB,EAAE,gBAAgB,EAAE,aAAa,EAAE,kBAAkB,EAAE,MAAM,kBAAkB,CAAC;AAC5G,OAAO,EAAE,aAAa,EAAE,gBAAgB,EAAE,iBAAiB,EAAE,YAAY,EAAE,mBAAmB,EAAE,kBAAkB,EAAE,MAAM,kBAAkB,CAAC;AAC7I,OAAO,EACL,cAAc,EAAE,cAAc,EAAE,cAAc,EAAE,YAAY,EAAE,SAAS,EAAE,QAAQ,EAAE,WAAW,EAC9F,gBAAgB,EAAE,gBAAgB,EAAE,iBAAiB,GACtD,MAAM,cAAc,CAAC;AACtB,YAAY,EAAE,eAAe,EAAE,WAAW,EAAE,WAAW,EAAE,aAAa,EAAE,MAAM,cAAc,CAAC;AAC7F,OAAO,EAAE,UAAU,EAAE,SAAS,EAAE,gBAAgB,EAAE,gBAAgB,EAAE,MAAM,kBAAkB,CAAC;AAC7F,YAAY,EAAE,WAAW,EAAE,WAAW,EAAE,MAAM,kBAAkB,CAAC;AACjE,OAAO,EAAE,OAAO,EAAE,SAAS,EAAE,MAAM,cAAc,CAAC;AAClD,YAAY,EAAE,MAAM,EAAE,WAAW,EAAE,OAAO,EAAE,MAAM,cAAc,CAAC;AACjE,OAAO,EAAE,QAAQ,EAAE,cAAc,EAAE,cAAc,EAAE,SAAS,EAAE,MAAM,WAAW,CAAC;AAChF,YAAY,EAAE,aAAa,EAAE,MAAM,WAAW,CAAC;AAC/C,OAAO,EACL,QAAQ,EAAE,eAAe,EAAE,kBAAkB,EAAE,gBAAgB,EAAE,kBAAkB,EAAE,mBAAmB,EACxG,cAAc,EAAE,aAAa,EAAE,mBAAmB,EAAE,mBAAmB,EAAE,SAAS,EAClF,iBAAiB,EAAE,kBAAkB,EAAE,gBAAgB,EAAE,sBAAsB,EAAE,mBAAmB,EACpG,aAAa,EAAE,iBAAiB,EAAE,sBAAsB,EAAE,mBAAmB,EAC7E,sBAAsB,EAAE,mBAAmB,EAC3C,gBAAgB,EAAE,wBAAwB,EAAE,aAAa,EAAE,cAAc,EAAE,mBAAmB,GAC/F,MAAM,cAAc,CAAC;AACtB,YAAY,EAAE,OAAO,EAAE,aAAa,EAAE,QAAQ,EAAE,MAAM,cAAc,CAAC;AACrE,OAAO,EAAE,WAAW,EAAE,cAAc,EAAE,UAAU,EAAE,MAAM,cAAc,CAAC;AACvE,YAAY,EAAE,gBAAgB,EAAE,aAAa,EAAE,aAAa,EAAE,aAAa,EAAE,MAAM,cAAc,CAAC;AAClG,OAAO,EACL,oBAAoB,EAAE,eAAe,EAAE,iBAAiB,EAAE,WAAW,EAAE,aAAa,EAAE,aAAa,GACpG,MAAM,sBAAsB,CAAC;AAC9B,YAAY,EAAE,SAAS,EAAE,MAAM,sBAAsB,CAAC;AACtD,OAAO,EAAE,YAAY,EAAE,cAAc,EAAE,cAAc,EAAE,YAAY,EAAE,cAAc,EAAE,MAAM,YAAY,CAAC;AACxG,YAAY,EAAE,cAAc,EAAE,MAAM,YAAY,CAAC;AACjD,OAAO,EAAE,YAAY,EAAE,UAAU,EAAE,aAAa,EAAE,MAAM,EAAE,MAAM,EAAE,WAAW,EAAE,YAAY,EAAE,qBAAqB,EAAE,MAAM,eAAe,CAAC;AAC1I,OAAO,EACL,WAAW,EAAE,eAAe,EAAE,iBAAiB,EAAE,eAAe,EAAE,eAAe,EAAE,iBAAiB,EAAE,YAAY,EAClH,YAAY,EAAE,cAAc,EAAE,iBAAiB,EAAE,iBAAiB,EAAE,sBAAsB,EAAE,YAAY,EACxG,sBAAsB,EAAE,qBAAqB,EAAE,eAAe,EAAE,oBAAoB,GACrF,MAAM,kBAAkB,CAAC;AAC1B,YAAY,EAAE,kBAAkB,EAAE,iBAAiB,EAAE,iBAAiB,EAAE,UAAU,EAAE,SAAS,EAAE,MAAM,kBAAkB,CAAC;AACxH,OAAO,EAAE,gBAAgB,EAAE,cAAc,EAAE,MAAM,gBAAgB,CAAC;AAClE,OAAO,EAAE,aAAa,EAAE,eAAe,EAAE,iBAAiB,EAAE,MAAM,iBAAiB,CAAC;AACpF,OAAO,EAAE,gBAAgB,EAAE,iBAAiB,EAAE,oBAAoB,EAAE,kBAAkB,EAAE,kBAAkB,EAAE,MAAM,kBAAkB,CAAC;AACrI,OAAO,EAAE,kBAAkB,EAAE,oBAAoB,EAAE,MAAM,cAAc,CAAC;AACxE,OAAO,EACL,qBAAqB,EAAE,UAAU,EAAE,aAAa,EAAE,kBAAkB,EAAE,YAAY,EAAE,gBAAgB,EAAE,eAAe,EAAE,gBAAgB,EAAE,OAAO,EAAE,cAAc,EAAE,eAAe,EAAE,SAAS,EAAE,OAAO,GACtM,MAAM,gBAAgB,CAAC;AACxB,YAAY,EAAE,WAAW,EAAE,QAAQ,EAAE,YAAY,EAAE,UAAU,EAAE,aAAa,EAAE,WAAW,EAAE,MAAM,gBAAgB,CAAC;AAClH,OAAO,EAAE,oBAAoB,EAAE,aAAa,EAAE,oBAAoB,EAAE,gBAAgB,EAAE,cAAc,EAAE,WAAW,EAAE,YAAY,EAAE,MAAM,kBAAkB,CAAC;AAC1J,YAAY,EAAE,SAAS,EAAE,YAAY,EAAE,WAAW,EAAE,aAAa,EAAE,mBAAmB,EAAE,MAAM,kBAAkB,CAAC;AACjH,OAAO,EAAE,WAAW,EAAE,MAAM,mBAAmB,CAAC;AAChD,YAAY,EAAE,QAAQ,EAAE,OAAO,EAAE,MAAM,mBAAmB,CAAC"}
package/dist/index.js CHANGED
@@ -12,6 +12,8 @@ export { computeManifest, renderManifest, cellState, cellKey, vectorsForCell, ve
12
12
  export { runProofFixtures, deliverProofReceipts, loadProofs, saveProofs, PROOF_FILE } from "./proof.js";
13
13
  export { inspectBudgetExport, loadBudgetExport, localBudgetOf, BUDGET_EXPORT_TYPE } from "./budget-load.js";
14
14
  export { inspectExport, loadPolicyExport, policyScopeDigest, policyBuilds, POLICY_SCOPE_DOMAIN, LOADED_POLICY_FILE } from "./policy-load.js";
15
+ export { inspectManaged, compileManaged, installManaged, restoreLocal, isManaged, readMeta, digestRules, MANAGED_RULE_IDS, MANAGED_DOC_FILE, MANAGED_META_FILE, } from "./managed.js";
16
+ export { syncPolicy, syncIfDue, SYNC_INTERVAL_MS, INLINE_BUDGET_MS } from "./policy-sync.js";
15
17
  export { VECTORS, mapVector } from "./vectors.js";
16
18
  export { scaffold, harnessSnippet, installHarness, placeHook } from "./init.js";
17
19
  export { userHome, userHarnessFile, projectHarnessFile, resolveConfigDir, writeHarnessConfig, removeHarnessConfig, cursorDetected, codexDetected, absoluteHookCommand, isHarnessConfigured, purgeHome, readHarnessConfig, trustProjectPolicy, isTrustedProject, untrustedProjectPolicy, trustedProjectsFile, harnessScopes, harnessScopeLabel, configuredHookCommands, hookCommandResolves, isScopebondHookCommand, harnessEntryMatches, localHarnessFile, isMachineSpecificCommand, gitShareState, excludeFromGit, pruneHarnessEntries, } from "./install.js";
package/dist/index.js.map CHANGED
@@ -1 +1 @@
1
- {"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"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","sourcesContent":["// @scopebond/hook — the Claude Code, Cursor + Codex connector. The library surface is\n// the deterministic mapper and the check-only runtime; the `scopebond-hook`\n// binary is the thin harness adapter over them.\n\nexport { mapClaudeToolUse, mapCodexToolUse, mapCursorEvent, fillPushBranch } from \"./map.js\";\nexport type { Mapped, NormalizedIntent } from \"./map.js\";\nexport { createHookRuntime, starterPolicy, upgradeStarterPolicy } from \"./runtime.js\";\nexport type { RuntimeConfig, Decision } from \"./runtime.js\";\nexport {\n compile, defaultRules, describeRules, loadRules, saveRules, rulesPath, pathRuleFor, RULES_FILE,\n} from \"./rules.js\";\nexport type { RuleSet, PathRule } from \"./rules.js\";\nexport { pathRuleMatches, isDestructiveProgram, isProtectedBranch } from \"./rules.js\";\nexport { classifyIntent, classificationBlocks, BLOCKING } from \"./classify.js\";\nexport type { CatalogId, Classification } from \"./classify.js\";\nexport { classifyRoot, applyRootScope } from \"./paths.js\";\nexport type { RootScope, RootOptions } from \"./paths.js\";\nexport { actionGroupId, withActionGroup, distinctTargets, ACTION_GROUP_PARAM, ACTION_GROUP_SIZE_PARAM, ACTION_GROUP_SEQ_PARAM } from \"./group.js\";\nexport { computeManifest, renderManifest, cellState, cellKey, vectorsForCell, vectorDigest } from \"./capabilities.js\";\nexport type { Manifest, CapabilityCell, CapabilityState, ProofRecord, HostVariant, EventPhase, ManifestInput } from \"./capabilities.js\";\nexport { runProofFixtures, deliverProofReceipts, loadProofs, saveProofs, PROOF_FILE } from \"./proof.js\";\nexport { inspectBudgetExport, loadBudgetExport, localBudgetOf, BUDGET_EXPORT_TYPE } from \"./budget-load.js\";\nexport { inspectExport, loadPolicyExport, policyScopeDigest, policyBuilds, POLICY_SCOPE_DOMAIN, LOADED_POLICY_FILE } from \"./policy-load.js\";\nexport { VECTORS, mapVector } from \"./vectors.js\";\nexport type { Vector, VectorAgent, Dialect } from \"./vectors.js\";\nexport { scaffold, harnessSnippet, installHarness, placeHook } from \"./init.js\";\nexport type { HookPlacement } from \"./init.js\";\nexport {\n userHome, userHarnessFile, projectHarnessFile, resolveConfigDir, writeHarnessConfig, removeHarnessConfig,\n cursorDetected, codexDetected, absoluteHookCommand, isHarnessConfigured, purgeHome,\n readHarnessConfig, trustProjectPolicy, isTrustedProject, untrustedProjectPolicy, trustedProjectsFile,\n harnessScopes, harnessScopeLabel, configuredHookCommands, hookCommandResolves,\n isScopebondHookCommand, harnessEntryMatches,\n localHarnessFile, isMachineSpecificCommand, gitShareState, excludeFromGit, pruneHarnessEntries,\n} from \"./install.js\";\nexport type { Harness, HarnessScopes, GitShare } from \"./install.js\";\nexport { explainDeny, describeAction, findClause } from \"./explain.js\";\nexport type { ExplainDenyInput, ExplainIntent, ExplainPolicy, ExplainClause } from \"./explain.js\";\nexport {\n ensureDurableRuntime, isEphemeralPath, nodeModulesRootOf, runtimeRoot, runtimeDirFor, pinnedCliPath,\n} from \"./runtime-install.js\";\nexport type { PinResult } from \"./runtime-install.js\";\nexport { connectCloud, loadConnection, attachExporter, flushBounded, connectionPath } from \"./cloud.js\";\nexport type { HookConnection } from \"./cloud.js\";\nexport { scrubSecrets, scrubParam, redactCommand, digest, sha256, keyedDigest, useDigestKey, loadOrCreateDigestKey } from \"./minimize.js\";\nexport {\n AGENT_KINDS, assertAgentKind, observationSigner, signObservation, observationHash, sourceReceiptHash, signingBytes,\n buildPayload, buildOperation, operationsForCall, bindingKeyFromHex, loadOrCreateBindingKey, digestPolicy,\n REQUEST_BINDING_DOMAIN, MAX_OBSERVATION_BYTES, MAX_BATCH_ITEMS, MAX_BATCH_BODY_BYTES,\n} from \"./observation.js\";\nexport type { ObservationPayload, SignedObservation, ObservationSigner, BindingKey, AgentKind } from \"./observation.js\";\nexport { ObservationStore, OBSERVATION_DB } from \"./obs-store.js\";\nexport { uploadPending, parseRetryAfter, OBSERVATIONS_PATH } from \"./obs-upload.js\";\nexport { openObservations, observationStatus, describeObservations, ObservationEmitter, OBSERVATIONS_SCOPE } from \"./obs-emitter.js\";\nexport { wireLifecycleHooks, unwireLifecycleHooks } from \"./install.js\";\nexport {\n deriveTypedOperations, keyedIdFor, callRequestOf, TYPED_ACTION_TYPES, fixtureProbe, gitPushOperation, githubOperation, packageOperation, parseGh, parseGithubMcp, normalizeRemote, systemGit, UNBOUND,\n} from \"./typed-ops.js\";\nexport type { CallRequest, GitProbe, TypedContext, GithubRepo, GithubRequest, DeriveInput } from \"./typed-ops.js\";\nexport { deriveInfraOperation, databaseFacts, databaseGuardActions, parseDestination, fetchOperation, systemFiles, fixtureFiles } from \"./typed-infra.js\";\nexport type { FileProbe, InfraContext, ReadContext, DatabaseFacts, DatabaseGuardAction } from \"./typed-infra.js\";\nexport { classifySql } from \"./sql-classify.js\";\nexport type { SqlClass, SqlVerb } from \"./sql-classify.js\";\n"]}
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"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","sourcesContent":["// @scopebond/hook — the Claude Code, Cursor + Codex connector. The library surface is\n// the deterministic mapper and the check-only runtime; the `scopebond-hook`\n// binary is the thin harness adapter over them.\n\nexport { mapClaudeToolUse, mapCodexToolUse, mapCursorEvent, fillPushBranch } from \"./map.js\";\nexport type { Mapped, NormalizedIntent } from \"./map.js\";\nexport { createHookRuntime, starterPolicy, upgradeStarterPolicy } from \"./runtime.js\";\nexport type { RuntimeConfig, Decision } from \"./runtime.js\";\nexport {\n compile, defaultRules, describeRules, loadRules, saveRules, rulesPath, pathRuleFor, RULES_FILE,\n} from \"./rules.js\";\nexport type { RuleSet, PathRule } from \"./rules.js\";\nexport { pathRuleMatches, isDestructiveProgram, isProtectedBranch } from \"./rules.js\";\nexport { classifyIntent, classificationBlocks, BLOCKING } from \"./classify.js\";\nexport type { CatalogId, Classification } from \"./classify.js\";\nexport { classifyRoot, applyRootScope } from \"./paths.js\";\nexport type { RootScope, RootOptions } from \"./paths.js\";\nexport { actionGroupId, withActionGroup, distinctTargets, ACTION_GROUP_PARAM, ACTION_GROUP_SIZE_PARAM, ACTION_GROUP_SEQ_PARAM } from \"./group.js\";\nexport { computeManifest, renderManifest, cellState, cellKey, vectorsForCell, vectorDigest } from \"./capabilities.js\";\nexport type { Manifest, CapabilityCell, CapabilityState, ProofRecord, HostVariant, EventPhase, ManifestInput } from \"./capabilities.js\";\nexport { runProofFixtures, deliverProofReceipts, loadProofs, saveProofs, PROOF_FILE } from \"./proof.js\";\nexport { inspectBudgetExport, loadBudgetExport, localBudgetOf, BUDGET_EXPORT_TYPE } from \"./budget-load.js\";\nexport { inspectExport, loadPolicyExport, policyScopeDigest, policyBuilds, POLICY_SCOPE_DOMAIN, LOADED_POLICY_FILE } from \"./policy-load.js\";\nexport {\n inspectManaged, compileManaged, installManaged, restoreLocal, isManaged, readMeta, digestRules,\n MANAGED_RULE_IDS, MANAGED_DOC_FILE, MANAGED_META_FILE,\n} from \"./managed.js\";\nexport type { ManagedDocument, ManagedRule, ManagedMeta, RefusalReason } from \"./managed.js\";\nexport { syncPolicy, syncIfDue, SYNC_INTERVAL_MS, INLINE_BUDGET_MS } from \"./policy-sync.js\";\nexport type { SyncOutcome, SyncOptions } from \"./policy-sync.js\";\nexport { VECTORS, mapVector } from \"./vectors.js\";\nexport type { Vector, VectorAgent, Dialect } from \"./vectors.js\";\nexport { scaffold, harnessSnippet, installHarness, placeHook } from \"./init.js\";\nexport type { HookPlacement } from \"./init.js\";\nexport {\n userHome, userHarnessFile, projectHarnessFile, resolveConfigDir, writeHarnessConfig, removeHarnessConfig,\n cursorDetected, codexDetected, absoluteHookCommand, isHarnessConfigured, purgeHome,\n readHarnessConfig, trustProjectPolicy, isTrustedProject, untrustedProjectPolicy, trustedProjectsFile,\n harnessScopes, harnessScopeLabel, configuredHookCommands, hookCommandResolves,\n isScopebondHookCommand, harnessEntryMatches,\n localHarnessFile, isMachineSpecificCommand, gitShareState, excludeFromGit, pruneHarnessEntries,\n} from \"./install.js\";\nexport type { Harness, HarnessScopes, GitShare } from \"./install.js\";\nexport { explainDeny, describeAction, findClause } from \"./explain.js\";\nexport type { ExplainDenyInput, ExplainIntent, ExplainPolicy, ExplainClause } from \"./explain.js\";\nexport {\n ensureDurableRuntime, isEphemeralPath, nodeModulesRootOf, runtimeRoot, runtimeDirFor, pinnedCliPath,\n} from \"./runtime-install.js\";\nexport type { PinResult } from \"./runtime-install.js\";\nexport { connectCloud, loadConnection, attachExporter, flushBounded, connectionPath } from \"./cloud.js\";\nexport type { HookConnection } from \"./cloud.js\";\nexport { scrubSecrets, scrubParam, redactCommand, digest, sha256, keyedDigest, useDigestKey, loadOrCreateDigestKey } from \"./minimize.js\";\nexport {\n AGENT_KINDS, assertAgentKind, observationSigner, signObservation, observationHash, sourceReceiptHash, signingBytes,\n buildPayload, buildOperation, operationsForCall, bindingKeyFromHex, loadOrCreateBindingKey, digestPolicy,\n REQUEST_BINDING_DOMAIN, MAX_OBSERVATION_BYTES, MAX_BATCH_ITEMS, MAX_BATCH_BODY_BYTES,\n} from \"./observation.js\";\nexport type { ObservationPayload, SignedObservation, ObservationSigner, BindingKey, AgentKind } from \"./observation.js\";\nexport { ObservationStore, OBSERVATION_DB } from \"./obs-store.js\";\nexport { uploadPending, parseRetryAfter, OBSERVATIONS_PATH } from \"./obs-upload.js\";\nexport { openObservations, observationStatus, describeObservations, ObservationEmitter, OBSERVATIONS_SCOPE } from \"./obs-emitter.js\";\nexport { wireLifecycleHooks, unwireLifecycleHooks } from \"./install.js\";\nexport {\n deriveTypedOperations, keyedIdFor, callRequestOf, TYPED_ACTION_TYPES, fixtureProbe, gitPushOperation, githubOperation, packageOperation, parseGh, parseGithubMcp, normalizeRemote, systemGit, UNBOUND,\n} from \"./typed-ops.js\";\nexport type { CallRequest, GitProbe, TypedContext, GithubRepo, GithubRequest, DeriveInput } from \"./typed-ops.js\";\nexport { deriveInfraOperation, databaseFacts, databaseGuardActions, parseDestination, fetchOperation, systemFiles, fixtureFiles } from \"./typed-infra.js\";\nexport type { FileProbe, InfraContext, ReadContext, DatabaseFacts, DatabaseGuardAction } from \"./typed-infra.js\";\nexport { classifySql } from \"./sql-classify.js\";\nexport type { SqlClass, SqlVerb } from \"./sql-classify.js\";\n"]}
@@ -0,0 +1,69 @@
1
+ import { type RuleSet } from "./rules.js";
2
+ export declare const MANAGED_DOC_FILE = "managed-rules.json";
3
+ export declare const MANAGED_META_FILE = "managed-meta.json";
4
+ export declare const PREVIOUS_POLICY_FILE = "policy.previous.json";
5
+ export declare const MANAGED_RULE_IDS: readonly ["force-push-protected", "push-protected", "destructive-shell", "secret-read", "ci-config-write", "network-egress"];
6
+ export type ManagedRuleId = (typeof MANAGED_RULE_IDS)[number];
7
+ type ListKey = "protected_branches" | "destructive_programs" | "allowed_hosts";
8
+ export type ManagedRule = {
9
+ mode: "monitor" | "block";
10
+ } & Partial<Record<ListKey, string[]>>;
11
+ export interface ManagedDocument {
12
+ type: "scopebond:managed-rules";
13
+ version: 1;
14
+ revision: number;
15
+ export_id: string;
16
+ environment_id: string;
17
+ agent_id: string;
18
+ installation_id: string;
19
+ rules: Record<ManagedRuleId, ManagedRule>;
20
+ rules_digest: string;
21
+ }
22
+ export type RefusalReason = "invalid_document" | "stale_revision" | "wrong_computer" | "unsupported" | "write_failed";
23
+ export type Inspection = {
24
+ ok: true;
25
+ doc: ManagedDocument;
26
+ } | {
27
+ ok: false;
28
+ reason: RefusalReason;
29
+ message: string;
30
+ };
31
+ export declare const digestRules: (rules: unknown) => string;
32
+ /** Check a document from the workspace without touching disk. Pure. */
33
+ export declare function inspectManaged(raw: unknown, context: {
34
+ installationId: string;
35
+ currentRevision: number | null;
36
+ }): Inspection;
37
+ /** The policy this computer enforces under workspace rules: its own compiled rules, adjusted rule by rule. Pure. */
38
+ export declare function compileManaged(local: RuleSet, doc: ManagedDocument, agentKid: string): Record<string, unknown>;
39
+ export interface ManagedMeta {
40
+ revision: number | null;
41
+ rules_digest: string | null;
42
+ export_id: string | null;
43
+ etag: string | null;
44
+ checked_at: string | null;
45
+ last_ack: {
46
+ revision: number;
47
+ result: "loaded" | "rejected";
48
+ at: string;
49
+ } | null;
50
+ last_error: string | null;
51
+ }
52
+ export declare function readMeta(dir: string): ManagedMeta;
53
+ export declare function writeMeta(dir: string, meta: ManagedMeta): void;
54
+ /** Whether a workspace currently manages the rules on this computer. */
55
+ export declare const isManaged: (dir: string) => boolean;
56
+ export declare function writeAtomic(file: string, text: string): void;
57
+ /** Install a checked document: write the merged policy (previous kept) and the document. `policyBuilds` is the caller's check
58
+ * that the gateway can load the result. */
59
+ export declare function installManaged(dir: string, doc: ManagedDocument, agentKid: string, policyBuilds: (policy: Record<string, unknown>) => boolean): {
60
+ ok: true;
61
+ } | {
62
+ ok: false;
63
+ reason: RefusalReason;
64
+ message: string;
65
+ };
66
+ /** Go back to this computer's own rules: recompile policy.json from rules.json and forget the workspace document. */
67
+ export declare function restoreLocal(dir: string, agentKid: string): void;
68
+ export {};
69
+ //# sourceMappingURL=managed.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"managed.d.ts","sourceRoot":"","sources":["../src/managed.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,184 @@
1
+ // Rules managed from a Scopebond Cloud workspace.
2
+ //
3
+ // A connected computer keeps its own rules (`rules.json` compiled into `policy.json`) until the workspace sets rules for it.
4
+ // From then on the workspace decides, rule by rule, whether a matching action is blocked or only recorded, and may add entries
5
+ // to the computer's own lists (protected branches, programs, allowed sites). It never sends a pattern: this module compiles the
6
+ // workspace's choices with the same compiler `rules apply` uses, so what decides a block lives in one place.
7
+ //
8
+ // What a workspace cannot change: the protection of the hook's own settings (and the settings of the agents it guards), the
9
+ // machine key policy, fail-closed handling of anything unparseable, and the computer's own opt-in extras (`allowed_roots`,
10
+ // `protect_remote_database`). A rule set to Monitor is still covered by an allow clause, so the action is recorded and passes;
11
+ // leaving it out would block the whole action type, because the verifier denies an action no clause covers.
12
+ //
13
+ // Loading is checked and atomic: the document must be well formed, name this computer, carry a matching digest and be newer
14
+ // than the one in force; the merged policy must build; policy.json is written through a temp file and a rename, the previous
15
+ // one kept. Disconnecting, or a workspace that stops managing this computer, restores the computer's own rules.
16
+ import { copyFileSync, existsSync, mkdirSync, readFileSync, renameSync, rmSync, writeFileSync } from "node:fs";
17
+ import { join } from "node:path";
18
+ import { createHash, randomBytes } from "node:crypto";
19
+ import { canonical } from "@scopebond/policy-schema/canonical";
20
+ import { GUARDRAIL_WRITE_PATTERN, ci } from "./runtime.js";
21
+ import { compile, defaultRules, loadRules } from "./rules.js";
22
+ export const MANAGED_DOC_FILE = "managed-rules.json";
23
+ export const MANAGED_META_FILE = "managed-meta.json";
24
+ export const PREVIOUS_POLICY_FILE = "policy.previous.json";
25
+ export const MANAGED_RULE_IDS = ["force-push-protected", "push-protected", "destructive-shell", "secret-read", "ci-config-write", "network-egress"];
26
+ const LIST_OF = {
27
+ "force-push-protected": "protected_branches", "push-protected": "protected_branches",
28
+ "destructive-shell": "destructive_programs", "network-egress": "allowed_hosts",
29
+ };
30
+ const LIST_PATTERN = {
31
+ protected_branches: /^[A-Za-z0-9._/*-]{1,100}$/,
32
+ destructive_programs: /^[a-z0-9][a-z0-9._-]{0,63}$/,
33
+ allowed_hosts: /^(?=.{1,253}$)(\*\.)?([a-z0-9]([a-z0-9-]{0,61}[a-z0-9])?\.)+[a-z]{2,63}$/,
34
+ };
35
+ const HEX64 = /^[0-9a-f]{64}$/;
36
+ const OPAQUE = /^[A-Za-z0-9._:-]{1,128}$/;
37
+ export const digestRules = (rules) => createHash("sha256").update(canonical(rules)).digest("hex");
38
+ const isObject = (v) => typeof v === "object" && v !== null && !Array.isArray(v);
39
+ /** Check a document from the workspace without touching disk. Pure. */
40
+ export function inspectManaged(raw, context) {
41
+ const bad = (message) => ({ ok: false, reason: "invalid_document", message });
42
+ if (!isObject(raw) || raw.type !== "scopebond:managed-rules")
43
+ return bad("this is not a Scopebond workspace rules document");
44
+ if (raw.version !== 1)
45
+ return { ok: false, reason: "unsupported", message: `unsupported rules document version ${String(raw.version)}` };
46
+ const { revision, export_id: exportId, environment_id: environmentId, agent_id: agentId, installation_id: installationId, rules, rules_digest: rulesDigest } = raw;
47
+ if (!Number.isInteger(revision) || revision < 1 || revision > 2_147_483_647
48
+ || typeof exportId !== "string" || typeof environmentId !== "string" || !OPAQUE.test(environmentId)
49
+ || typeof agentId !== "string" || !OPAQUE.test(agentId) || typeof installationId !== "string" || !OPAQUE.test(installationId)
50
+ || typeof rulesDigest !== "string" || !HEX64.test(rulesDigest) || !isObject(rules)) {
51
+ return bad("the rules document is incomplete or malformed");
52
+ }
53
+ if (installationId !== context.installationId || exportId !== `rev-${revision}-${installationId}`) {
54
+ return { ok: false, reason: "wrong_computer", message: "these rules were issued for a different computer" };
55
+ }
56
+ const keys = Object.keys(rules);
57
+ if (keys.length !== MANAGED_RULE_IDS.length || !MANAGED_RULE_IDS.every((id) => keys.includes(id))) {
58
+ return { ok: false, reason: "unsupported", message: "the rules document names rules this version does not know; update Scopebond" };
59
+ }
60
+ for (const id of MANAGED_RULE_IDS) {
61
+ const rule = rules[id];
62
+ if (!isObject(rule) || (rule.mode !== "monitor" && rule.mode !== "block"))
63
+ return bad(`rule ${id} has no valid mode`);
64
+ for (const key of Object.keys(rule)) {
65
+ if (key === "mode")
66
+ continue;
67
+ const listKey = LIST_OF[id];
68
+ if (key !== listKey)
69
+ return bad(`rule ${id} has an unknown setting`);
70
+ const list = rule[key];
71
+ if (!Array.isArray(list) || list.length > 50 || !list.every((v) => typeof v === "string" && LIST_PATTERN[listKey].test(v) && !v.includes(".."))) {
72
+ return bad(`rule ${id} has an invalid list`);
73
+ }
74
+ }
75
+ }
76
+ const network = rules["network-egress"];
77
+ if (network.mode === "block" && !(network.allowed_hosts?.length))
78
+ return bad("network blocking needs at least one allowed site");
79
+ if (digestRules(rules) !== rulesDigest)
80
+ return bad("the rules do not match their digest; the document was changed or damaged");
81
+ if (context.currentRevision !== null && revision <= context.currentRevision) {
82
+ return { ok: false, reason: "stale_revision", message: `version ${String(revision)} is not newer than the version in force (${context.currentRevision})` };
83
+ }
84
+ return { ok: true, doc: raw };
85
+ }
86
+ const union = (a, b) => [...new Set([...a, ...(b ?? [])])];
87
+ const branchGlob = (b) => (b.endsWith("/*") ? `${b.slice(0, -1)}**` : b);
88
+ const hostPattern = (hosts) => `^(?:${hosts.map((h) => (h.startsWith("*.") ? `(?:[^.]+\\.)+${ci(h.slice(2).replace(/\./g, "\\."))}` : ci(h.replace(/\./g, "\\.")))).join("|")})$`;
89
+ /** The policy this computer enforces under workspace rules: its own compiled rules, adjusted rule by rule. Pure. */
90
+ export function compileManaged(local, doc, agentKid) {
91
+ const r = doc.rules;
92
+ const rules = {
93
+ ...local,
94
+ protected_branches: union(local.protected_branches, [...(r["push-protected"].protected_branches ?? []), ...(r["force-push-protected"].protected_branches ?? [])]),
95
+ destructive_programs: union(local.destructive_programs, r["destructive-shell"].destructive_programs),
96
+ };
97
+ const policy = compile(rules, agentKid);
98
+ const allowAll = (clause) => { const { param_bounds: _dropped, ...rest } = clause; return { ...rest, mode: "enforce", description: `${String(clause.description ?? "").split(".")[0]}. Recorded, not blocked: set by your workspace.` }; };
99
+ const clauses = [];
100
+ for (const clause of policy.clauses) {
101
+ if (clause.id === "protect-branches" && r["push-protected"].mode === "monitor") {
102
+ clauses.push(allowAll(clause));
103
+ if (r["force-push-protected"].mode === "block") {
104
+ clauses.push({ id: "protect-branch-history", type: "force_push_guard", mode: "enforce", protected_refs: rules.protected_branches.map(branchGlob),
105
+ description: "Deny force-pushes, deletions and mirror pushes that reach a protected branch; ordinary pushes are allowed. Set by your workspace." });
106
+ }
107
+ }
108
+ else if (clause.id === "safe-shell" && r["destructive-shell"].mode === "monitor")
109
+ clauses.push(allowAll(clause));
110
+ else if (clause.id === "protect-read" && r["secret-read"].mode === "monitor")
111
+ clauses.push(allowAll(clause));
112
+ else if (clause.id === "protect-write" && r["ci-config-write"].mode === "monitor") {
113
+ clauses.push({ ...clause, param_bounds: { path: { pattern: GUARDRAIL_WRITE_PATTERN } },
114
+ description: "Allow workspace writes, but never to Scopebond's own settings or the agents' hook settings (always on). Other protected files are recorded, not blocked: set by your workspace." });
115
+ }
116
+ else
117
+ clauses.push(clause);
118
+ }
119
+ if (r["network-egress"].mode === "block") {
120
+ const keysAt = clauses.findIndex((c) => c.id === "keys");
121
+ clauses.splice(keysAt < 0 ? clauses.length : keysAt, 0, {
122
+ id: "allowed-sites", type: "action_allowlist", mode: "enforce", action_types: ["net.fetch"],
123
+ param_bounds: { host: { pattern: hostPattern(r["network-egress"].allowed_hosts ?? []) } },
124
+ description: `Deny web requests to sites other than ${r["network-egress"].allowed_hosts.join(", ")}. Set by your workspace.`,
125
+ });
126
+ }
127
+ return { ...policy, policy_id: "coding-agent", version: doc.revision, clauses };
128
+ }
129
+ const EMPTY_META = { revision: null, rules_digest: null, export_id: null, etag: null, checked_at: null, last_ack: null, last_error: null };
130
+ export function readMeta(dir) {
131
+ try {
132
+ return { ...EMPTY_META, ...JSON.parse(readFileSync(join(dir, MANAGED_META_FILE), "utf8")) };
133
+ }
134
+ catch {
135
+ return { ...EMPTY_META };
136
+ }
137
+ }
138
+ export function writeMeta(dir, meta) { writeAtomic(join(dir, MANAGED_META_FILE), `${JSON.stringify(meta, null, 2)}\n`); }
139
+ /** Whether a workspace currently manages the rules on this computer. */
140
+ export const isManaged = (dir) => existsSync(join(dir, MANAGED_DOC_FILE));
141
+ export function writeAtomic(file, text) {
142
+ const temp = `${file}.${randomBytes(6).toString("hex")}.tmp`;
143
+ try {
144
+ writeFileSync(temp, text, { mode: 0o600 });
145
+ renameSync(temp, file);
146
+ }
147
+ catch (error) {
148
+ try {
149
+ rmSync(temp, { force: true });
150
+ }
151
+ catch { /* nothing to remove */ }
152
+ throw error;
153
+ }
154
+ }
155
+ /** Install a checked document: write the merged policy (previous kept) and the document. `policyBuilds` is the caller's check
156
+ * that the gateway can load the result. */
157
+ export function installManaged(dir, doc, agentKid, policyBuilds) {
158
+ const local = loadRules(dir) ?? defaultRules();
159
+ const policy = compileManaged(local, doc, agentKid);
160
+ if (!policyBuilds(policy))
161
+ return { ok: false, reason: "unsupported", message: "this version of Scopebond cannot load the workspace rules; update it" };
162
+ try {
163
+ mkdirSync(dir, { recursive: true });
164
+ const policyPath = join(dir, "policy.json");
165
+ if (existsSync(policyPath))
166
+ copyFileSync(policyPath, join(dir, PREVIOUS_POLICY_FILE));
167
+ writeAtomic(policyPath, `${JSON.stringify(policy, null, 2)}\n`);
168
+ writeAtomic(join(dir, MANAGED_DOC_FILE), `${JSON.stringify(doc, null, 2)}\n`);
169
+ return { ok: true };
170
+ }
171
+ catch (error) {
172
+ return { ok: false, reason: "write_failed", message: `could not write the rules (${error.name})` };
173
+ }
174
+ }
175
+ /** Go back to this computer's own rules: recompile policy.json from rules.json and forget the workspace document. */
176
+ export function restoreLocal(dir, agentKid) {
177
+ const local = loadRules(dir) ?? defaultRules();
178
+ const policyPath = join(dir, "policy.json");
179
+ if (existsSync(policyPath))
180
+ copyFileSync(policyPath, join(dir, PREVIOUS_POLICY_FILE));
181
+ writeAtomic(policyPath, `${JSON.stringify(compile(local, agentKid), null, 2)}\n`);
182
+ rmSync(join(dir, MANAGED_DOC_FILE), { force: true });
183
+ }
184
+ //# sourceMappingURL=managed.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"managed.js","sourceRoot":"","sources":["../src/managed.ts"],"names":[],"mappings":"AAAA,kDAAkD;AAClD,EAAE;AACF,6HAA6H;AAC7H,+HAA+H;AAC/H,gIAAgI;AAChI,6GAA6G;AAC7G,EAAE;AACF,4HAA4H;AAC5H,2HAA2H;AAC3H,+HAA+H;AAC/H,4GAA4G;AAC5G,EAAE;AACF,4HAA4H;AAC5H,6HAA6H;AAC7H,gHAAgH;AAEhH,OAAO,EAAE,YAAY,EAAE,UAAU,EAAE,SAAS,EAAE,YAAY,EAAE,UAAU,EAAE,MAAM,EAAE,aAAa,EAAE,MAAM,SAAS,CAAC;AAC/G,OAAO,EAAE,IAAI,EAAE,MAAM,WAAW,CAAC;AACjC,OAAO,EAAE,UAAU,EAAE,WAAW,EAAE,MAAM,aAAa,CAAC;AACtD,OAAO,EAAE,SAAS,EAAE,MAAM,oCAAoC,CAAC;AAC/D,OAAO,EAAE,uBAAuB,EAAE,EAAE,EAAE,MAAM,cAAc,CAAC;AAC3D,OAAO,EAAE,OAAO,EAAE,YAAY,EAAE,SAAS,EAAgB,MAAM,YAAY,CAAC;AAE5E,MAAM,CAAC,MAAM,gBAAgB,GAAG,oBAAoB,CAAC;AACrD,MAAM,CAAC,MAAM,iBAAiB,GAAG,mBAAmB,CAAC;AACrD,MAAM,CAAC,MAAM,oBAAoB,GAAG,sBAAsB,CAAC;AAE3D,MAAM,CAAC,MAAM,gBAAgB,GAAG,CAAC,sBAAsB,EAAE,gBAAgB,EAAE,mBAAmB,EAAE,aAAa,EAAE,iBAAiB,EAAE,gBAAgB,CAAU,CAAC;AAG7J,MAAM,OAAO,GAA4C;IACvD,sBAAsB,EAAE,oBAAoB,EAAE,gBAAgB,EAAE,oBAAoB;IACpF,mBAAmB,EAAE,sBAAsB,EAAE,gBAAgB,EAAE,eAAe;CAC/E,CAAC;AACF,MAAM,YAAY,GAA4B;IAC5C,kBAAkB,EAAE,2BAA2B;IAC/C,oBAAoB,EAAE,6BAA6B;IACnD,aAAa,EAAE,0EAA0E;CAC1F,CAAC;AACF,MAAM,KAAK,GAAG,gBAAgB,CAAC;AAC/B,MAAM,MAAM,GAAG,0BAA0B,CAAC;AAkB1C,MAAM,CAAC,MAAM,WAAW,GAAG,CAAC,KAAc,EAAU,EAAE,CAAC,UAAU,CAAC,QAAQ,CAAC,CAAC,MAAM,CAAC,SAAS,CAAC,KAAK,CAAC,CAAC,CAAC,MAAM,CAAC,KAAK,CAAC,CAAC;AAEnH,MAAM,QAAQ,GAAG,CAAC,CAAU,EAAgC,EAAE,CAAC,OAAO,CAAC,KAAK,QAAQ,IAAI,CAAC,KAAK,IAAI,IAAI,CAAC,KAAK,CAAC,OAAO,CAAC,CAAC,CAAC,CAAC;AAExH,uEAAuE;AACvE,MAAM,UAAU,cAAc,CAAC,GAAY,EAAE,OAAmE;IAC9G,MAAM,GAAG,GAAG,CAAC,OAAe,EAAc,EAAE,CAAC,CAAC,EAAE,EAAE,EAAE,KAAK,EAAE,MAAM,EAAE,kBAAkB,EAAE,OAAO,EAAE,CAAC,CAAC;IAClG,IAAI,CAAC,QAAQ,CAAC,GAAG,CAAC,IAAI,GAAG,CAAC,IAAI,KAAK,yBAAyB;QAAE,OAAO,GAAG,CAAC,kDAAkD,CAAC,CAAC;IAC7H,IAAI,GAAG,CAAC,OAAO,KAAK,CAAC;QAAE,OAAO,EAAE,EAAE,EAAE,KAAK,EAAE,MAAM,EAAE,aAAa,EAAE,OAAO,EAAE,sCAAsC,MAAM,CAAC,GAAG,CAAC,OAAO,CAAC,EAAE,EAAE,CAAC;IACzI,MAAM,EAAE,QAAQ,EAAE,SAAS,EAAE,QAAQ,EAAE,cAAc,EAAE,aAAa,EAAE,QAAQ,EAAE,OAAO,EAAE,eAAe,EAAE,cAAc,EAAE,KAAK,EAAE,YAAY,EAAE,WAAW,EAAE,GAAG,GAAG,CAAC;IACnK,IAAI,CAAC,MAAM,CAAC,SAAS,CAAC,QAAQ,CAAC,IAAK,QAAmB,GAAG,CAAC,IAAK,QAAmB,GAAG,aAAa;WAC9F,OAAO,QAAQ,KAAK,QAAQ,IAAI,OAAO,aAAa,KAAK,QAAQ,IAAI,CAAC,MAAM,CAAC,IAAI,CAAC,aAAa,CAAC;WAChG,OAAO,OAAO,KAAK,QAAQ,IAAI,CAAC,MAAM,CAAC,IAAI,CAAC,OAAO,CAAC,IAAI,OAAO,cAAc,KAAK,QAAQ,IAAI,CAAC,MAAM,CAAC,IAAI,CAAC,cAAc,CAAC;WAC1H,OAAO,WAAW,KAAK,QAAQ,IAAI,CAAC,KAAK,CAAC,IAAI,CAAC,WAAW,CAAC,IAAI,CAAC,QAAQ,CAAC,KAAK,CAAC,EAAE,CAAC;QACrF,OAAO,GAAG,CAAC,+CAA+C,CAAC,CAAC;IAC9D,CAAC;IACD,IAAI,cAAc,KAAK,OAAO,CAAC,cAAc,IAAI,QAAQ,KAAK,OAAO,QAAQ,IAAI,cAAc,EAAE,EAAE,CAAC;QAClG,OAAO,EAAE,EAAE,EAAE,KAAK,EAAE,MAAM,EAAE,gBAAgB,EAAE,OAAO,EAAE,kDAAkD,EAAE,CAAC;IAC9G,CAAC;IACD,MAAM,IAAI,GAAG,MAAM,CAAC,IAAI,CAAC,KAAK,CAAC,CAAC;IAChC,IAAI,IAAI,CAAC,MAAM,KAAK,gBAAgB,CAAC,MAAM,IAAI,CAAC,gBAAgB,CAAC,KAAK,CAAC,CAAC,EAAE,EAAE,EAAE,CAAC,IAAI,CAAC,QAAQ,CAAC,EAAE,CAAC,CAAC,EAAE,CAAC;QAClG,OAAO,EAAE,EAAE,EAAE,KAAK,EAAE,MAAM,EAAE,aAAa,EAAE,OAAO,EAAE,6EAA6E,EAAE,CAAC;IACtI,CAAC;IACD,KAAK,MAAM,EAAE,IAAI,gBAAgB,EAAE,CAAC;QAClC,MAAM,IAAI,GAAG,KAAK,CAAC,EAAE,CAAC,CAAC;QACvB,IAAI,CAAC,QAAQ,CAAC,IAAI,CAAC,IAAI,CAAC,IAAI,CAAC,IAAI,KAAK,SAAS,IAAI,IAAI,CAAC,IAAI,KAAK,OAAO,CAAC;YAAE,OAAO,GAAG,CAAC,QAAQ,EAAE,oBAAoB,CAAC,CAAC;QACtH,KAAK,MAAM,GAAG,IAAI,MAAM,CAAC,IAAI,CAAC,IAAI,CAAC,EAAE,CAAC;YACpC,IAAI,GAAG,KAAK,MAAM;gBAAE,SAAS;YAC7B,MAAM,OAAO,GAAG,OAAO,CAAC,EAAE,CAAC,CAAC;YAC5B,IAAI,GAAG,KAAK,OAAO;gBAAE,OAAO,GAAG,CAAC,QAAQ,EAAE,yBAAyB,CAAC,CAAC;YACrE,MAAM,IAAI,GAAG,IAAI,CAAC,GAAG,CAAC,CAAC;YACvB,IAAI,CAAC,KAAK,CAAC,OAAO,CAAC,IAAI,CAAC,IAAI,IAAI,CAAC,MAAM,GAAG,EAAE,IAAI,CAAC,IAAI,CAAC,KAAK,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,OAAO,CAAC,KAAK,QAAQ,IAAI,YAAY,CAAC,OAAO,CAAC,CAAC,IAAI,CAAC,CAAC,CAAC,IAAI,CAAC,CAAC,CAAC,QAAQ,CAAC,IAAI,CAAC,CAAC,EAAE,CAAC;gBAChJ,OAAO,GAAG,CAAC,QAAQ,EAAE,sBAAsB,CAAC,CAAC;YAC/C,CAAC;QACH,CAAC;IACH,CAAC;IACD,MAAM,OAAO,GAAG,KAAK,CAAC,gBAAgB,CAAgB,CAAC;IACvD,IAAI,OAAO,CAAC,IAAI,KAAK,OAAO,IAAI,CAAC,CAAC,OAAO,CAAC,aAAa,EAAE,MAAM,CAAC;QAAE,OAAO,GAAG,CAAC,kDAAkD,CAAC,CAAC;IACjI,IAAI,WAAW,CAAC,KAAK,CAAC,KAAK,WAAW;QAAE,OAAO,GAAG,CAAC,0EAA0E,CAAC,CAAC;IAC/H,IAAI,OAAO,CAAC,eAAe,KAAK,IAAI,IAAK,QAAmB,IAAI,OAAO,CAAC,eAAe,EAAE,CAAC;QACxF,OAAO,EAAE,EAAE,EAAE,KAAK,EAAE,MAAM,EAAE,gBAAgB,EAAE,OAAO,EAAE,WAAW,MAAM,CAAC,QAAQ,CAAC,4CAA4C,OAAO,CAAC,eAAe,GAAG,EAAE,CAAC;IAC7J,CAAC;IACD,OAAO,EAAE,EAAE,EAAE,IAAI,EAAE,GAAG,EAAE,GAAiC,EAAE,CAAC;AAC9D,CAAC;AAED,MAAM,KAAK,GAAG,CAAC,CAAoB,EAAE,CAAgC,EAAY,EAAE,CAAC,CAAC,GAAG,IAAI,GAAG,CAAC,CAAC,GAAG,CAAC,EAAE,GAAG,CAAC,CAAC,IAAI,EAAE,CAAC,CAAC,CAAC,CAAC,CAAC;AACvH,MAAM,UAAU,GAAG,CAAC,CAAS,EAAU,EAAE,CAAC,CAAC,CAAC,CAAC,QAAQ,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC,GAAG,CAAC,CAAC,KAAK,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC;AACzF,MAAM,WAAW,GAAG,CAAC,KAAwB,EAAU,EAAE,CACvD,OAAO,KAAK,CAAC,GAAG,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,CAAC,UAAU,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC,gBAAgB,EAAE,CAAC,CAAC,CAAC,KAAK,CAAC,CAAC,CAAC,CAAC,OAAO,CAAC,KAAK,EAAE,KAAK,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,OAAO,CAAC,KAAK,EAAE,KAAK,CAAC,CAAC,CAAC,CAAC,CAAC,IAAI,CAAC,GAAG,CAAC,IAAI,CAAC;AAIrJ,oHAAoH;AACpH,MAAM,UAAU,cAAc,CAAC,KAAc,EAAE,GAAoB,EAAE,QAAgB;IACnF,MAAM,CAAC,GAAG,GAAG,CAAC,KAAK,CAAC;IACpB,MAAM,KAAK,GAAY;QACrB,GAAG,KAAK;QACR,kBAAkB,EAAE,KAAK,CAAC,KAAK,CAAC,kBAAkB,EAAE,CAAC,GAAG,CAAC,CAAC,CAAC,gBAAgB,CAAC,CAAC,kBAAkB,IAAI,EAAE,CAAC,EAAE,GAAG,CAAC,CAAC,CAAC,sBAAsB,CAAC,CAAC,kBAAkB,IAAI,EAAE,CAAC,CAAC,CAAC;QACjK,oBAAoB,EAAE,KAAK,CAAC,KAAK,CAAC,oBAAoB,EAAE,CAAC,CAAC,mBAAmB,CAAC,CAAC,oBAAoB,CAAC;KACrG,CAAC;IACF,MAAM,MAAM,GAAG,OAAO,CAAC,KAAK,EAAE,QAAQ,CAAoD,CAAC;IAC3F,MAAM,QAAQ,GAAG,CAAC,MAAc,EAAU,EAAE,GAAG,MAAM,EAAE,YAAY,EAAE,QAAQ,EAAE,GAAG,IAAI,EAAE,GAAG,MAAM,CAAC,CAAC,OAAO,EAAE,GAAG,IAAI,EAAE,IAAI,EAAE,SAAS,EAAE,WAAW,EAAE,GAAG,MAAM,CAAC,MAAM,CAAC,WAAW,IAAI,EAAE,CAAC,CAAC,KAAK,CAAC,GAAG,CAAC,CAAC,CAAC,CAAC,iDAAiD,EAAE,CAAC,CAAC,CAAC,CAAC;IAC3P,MAAM,OAAO,GAAa,EAAE,CAAC;IAC7B,KAAK,MAAM,MAAM,IAAI,MAAM,CAAC,OAAO,EAAE,CAAC;QACpC,IAAI,MAAM,CAAC,EAAE,KAAK,kBAAkB,IAAI,CAAC,CAAC,gBAAgB,CAAC,CAAC,IAAI,KAAK,SAAS,EAAE,CAAC;YAC/E,OAAO,CAAC,IAAI,CAAC,QAAQ,CAAC,MAAM,CAAC,CAAC,CAAC;YAC/B,IAAI,CAAC,CAAC,sBAAsB,CAAC,CAAC,IAAI,KAAK,OAAO,EAAE,CAAC;gBAC/C,OAAO,CAAC,IAAI,CAAC,EAAE,EAAE,EAAE,wBAAwB,EAAE,IAAI,EAAE,kBAAkB,EAAE,IAAI,EAAE,SAAS,EAAE,cAAc,EAAE,KAAK,CAAC,kBAAkB,CAAC,GAAG,CAAC,UAAU,CAAC;oBAC9I,WAAW,EAAE,mIAAmI,EAAE,CAAC,CAAC;YACxJ,CAAC;QACH,CAAC;aAAM,IAAI,MAAM,CAAC,EAAE,KAAK,YAAY,IAAI,CAAC,CAAC,mBAAmB,CAAC,CAAC,IAAI,KAAK,SAAS;YAAE,OAAO,CAAC,IAAI,CAAC,QAAQ,CAAC,MAAM,CAAC,CAAC,CAAC;aAC9G,IAAI,MAAM,CAAC,EAAE,KAAK,cAAc,IAAI,CAAC,CAAC,aAAa,CAAC,CAAC,IAAI,KAAK,SAAS;YAAE,OAAO,CAAC,IAAI,CAAC,QAAQ,CAAC,MAAM,CAAC,CAAC,CAAC;aACxG,IAAI,MAAM,CAAC,EAAE,KAAK,eAAe,IAAI,CAAC,CAAC,iBAAiB,CAAC,CAAC,IAAI,KAAK,SAAS,EAAE,CAAC;YAClF,OAAO,CAAC,IAAI,CAAC,EAAE,GAAG,MAAM,EAAE,YAAY,EAAE,EAAE,IAAI,EAAE,EAAE,OAAO,EAAE,uBAAuB,EAAE,EAAE;gBACpF,WAAW,EAAE,iLAAiL,EAAE,CAAC,CAAC;QACtM,CAAC;;YAAM,OAAO,CAAC,IAAI,CAAC,MAAM,CAAC,CAAC;IAC9B,CAAC;IACD,IAAI,CAAC,CAAC,gBAAgB,CAAC,CAAC,IAAI,KAAK,OAAO,EAAE,CAAC;QACzC,MAAM,MAAM,GAAG,OAAO,CAAC,SAAS,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,EAAE,KAAK,MAAM,CAAC,CAAC;QACzD,OAAO,CAAC,MAAM,CAAC,MAAM,GAAG,CAAC,CAAC,CAAC,CAAC,OAAO,CAAC,MAAM,CAAC,CAAC,CAAC,MAAM,EAAE,CAAC,EAAE;YACtD,EAAE,EAAE,eAAe,EAAE,IAAI,EAAE,kBAAkB,EAAE,IAAI,EAAE,SAAS,EAAE,YAAY,EAAE,CAAC,WAAW,CAAC;YAC3F,YAAY,EAAE,EAAE,IAAI,EAAE,EAAE,OAAO,EAAE,WAAW,CAAC,CAAC,CAAC,gBAAgB,CAAC,CAAC,aAAa,IAAI,EAAE,CAAC,EAAE,EAAE;YACzF,WAAW,EAAE,yCAAyC,CAAC,CAAC,gBAAgB,CAAC,CAAC,aAAc,CAAC,IAAI,CAAC,IAAI,CAAC,0BAA0B;SAC9H,CAAC,CAAC;IACL,CAAC;IACD,OAAO,EAAE,GAAG,MAAM,EAAE,SAAS,EAAE,cAAc,EAAE,OAAO,EAAE,GAAG,CAAC,QAAQ,EAAE,OAAO,EAAE,CAAC;AAClF,CAAC;AAaD,MAAM,UAAU,GAAgB,EAAE,QAAQ,EAAE,IAAI,EAAE,YAAY,EAAE,IAAI,EAAE,SAAS,EAAE,IAAI,EAAE,IAAI,EAAE,IAAI,EAAE,UAAU,EAAE,IAAI,EAAE,QAAQ,EAAE,IAAI,EAAE,UAAU,EAAE,IAAI,EAAE,CAAC;AAExJ,MAAM,UAAU,QAAQ,CAAC,GAAW;IAClC,IAAI,CAAC;QAAC,OAAO,EAAE,GAAG,UAAU,EAAE,GAAI,IAAI,CAAC,KAAK,CAAC,YAAY,CAAC,IAAI,CAAC,GAAG,EAAE,iBAAiB,CAAC,EAAE,MAAM,CAAC,CAA0B,EAAE,CAAC;IAAC,CAAC;IAAC,MAAM,CAAC;QAAC,OAAO,EAAE,GAAG,UAAU,EAAE,CAAC;IAAC,CAAC;AACpK,CAAC;AACD,MAAM,UAAU,SAAS,CAAC,GAAW,EAAE,IAAiB,IAAU,WAAW,CAAC,IAAI,CAAC,GAAG,EAAE,iBAAiB,CAAC,EAAE,GAAG,IAAI,CAAC,SAAS,CAAC,IAAI,EAAE,IAAI,EAAE,CAAC,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC;AAEpJ,wEAAwE;AACxE,MAAM,CAAC,MAAM,SAAS,GAAG,CAAC,GAAW,EAAW,EAAE,CAAC,UAAU,CAAC,IAAI,CAAC,GAAG,EAAE,gBAAgB,CAAC,CAAC,CAAC;AAE3F,MAAM,UAAU,WAAW,CAAC,IAAY,EAAE,IAAY;IACpD,MAAM,IAAI,GAAG,GAAG,IAAI,IAAI,WAAW,CAAC,CAAC,CAAC,CAAC,QAAQ,CAAC,KAAK,CAAC,MAAM,CAAC;IAC7D,IAAI,CAAC;QAAC,aAAa,CAAC,IAAI,EAAE,IAAI,EAAE,EAAE,IAAI,EAAE,KAAK,EAAE,CAAC,CAAC;QAAC,UAAU,CAAC,IAAI,EAAE,IAAI,CAAC,CAAC;IAAC,CAAC;IAC3E,OAAO,KAAK,EAAE,CAAC;QAAC,IAAI,CAAC;YAAC,MAAM,CAAC,IAAI,EAAE,EAAE,KAAK,EAAE,IAAI,EAAE,CAAC,CAAC;QAAC,CAAC;QAAC,MAAM,CAAC,CAAC,uBAAuB,CAAC,CAAC;QAAC,MAAM,KAAK,CAAC;IAAC,CAAC;AACzG,CAAC;AAED;4CAC4C;AAC5C,MAAM,UAAU,cAAc,CAAC,GAAW,EAAE,GAAoB,EAAE,QAAgB,EAAE,YAA0D;IAC5I,MAAM,KAAK,GAAG,SAAS,CAAC,GAAG,CAAC,IAAI,YAAY,EAAE,CAAC;IAC/C,MAAM,MAAM,GAAG,cAAc,CAAC,KAAK,EAAE,GAAG,EAAE,QAAQ,CAAC,CAAC;IACpD,IAAI,CAAC,YAAY,CAAC,MAAM,CAAC;QAAE,OAAO,EAAE,EAAE,EAAE,KAAK,EAAE,MAAM,EAAE,aAAa,EAAE,OAAO,EAAE,sEAAsE,EAAE,CAAC;IACxJ,IAAI,CAAC;QACH,SAAS,CAAC,GAAG,EAAE,EAAE,SAAS,EAAE,IAAI,EAAE,CAAC,CAAC;QACpC,MAAM,UAAU,GAAG,IAAI,CAAC,GAAG,EAAE,aAAa,CAAC,CAAC;QAC5C,IAAI,UAAU,CAAC,UAAU,CAAC;YAAE,YAAY,CAAC,UAAU,EAAE,IAAI,CAAC,GAAG,EAAE,oBAAoB,CAAC,CAAC,CAAC;QACtF,WAAW,CAAC,UAAU,EAAE,GAAG,IAAI,CAAC,SAAS,CAAC,MAAM,EAAE,IAAI,EAAE,CAAC,CAAC,IAAI,CAAC,CAAC;QAChE,WAAW,CAAC,IAAI,CAAC,GAAG,EAAE,gBAAgB,CAAC,EAAE,GAAG,IAAI,CAAC,SAAS,CAAC,GAAG,EAAE,IAAI,EAAE,CAAC,CAAC,IAAI,CAAC,CAAC;QAC9E,OAAO,EAAE,EAAE,EAAE,IAAI,EAAE,CAAC;IACtB,CAAC;IAAC,OAAO,KAAK,EAAE,CAAC;QACf,OAAO,EAAE,EAAE,EAAE,KAAK,EAAE,MAAM,EAAE,cAAc,EAAE,OAAO,EAAE,8BAA+B,KAAe,CAAC,IAAI,GAAG,EAAE,CAAC;IAChH,CAAC;AACH,CAAC;AAED,qHAAqH;AACrH,MAAM,UAAU,YAAY,CAAC,GAAW,EAAE,QAAgB;IACxD,MAAM,KAAK,GAAG,SAAS,CAAC,GAAG,CAAC,IAAI,YAAY,EAAE,CAAC;IAC/C,MAAM,UAAU,GAAG,IAAI,CAAC,GAAG,EAAE,aAAa,CAAC,CAAC;IAC5C,IAAI,UAAU,CAAC,UAAU,CAAC;QAAE,YAAY,CAAC,UAAU,EAAE,IAAI,CAAC,GAAG,EAAE,oBAAoB,CAAC,CAAC,CAAC;IACtF,WAAW,CAAC,UAAU,EAAE,GAAG,IAAI,CAAC,SAAS,CAAC,OAAO,CAAC,KAAK,EAAE,QAAQ,CAAC,EAAE,IAAI,EAAE,CAAC,CAAC,IAAI,CAAC,CAAC;IAClF,MAAM,CAAC,IAAI,CAAC,GAAG,EAAE,gBAAgB,CAAC,EAAE,EAAE,KAAK,EAAE,IAAI,EAAE,CAAC,CAAC;AACvD,CAAC","sourcesContent":["// Rules managed from a Scopebond Cloud workspace.\n//\n// A connected computer keeps its own rules (`rules.json` compiled into `policy.json`) until the workspace sets rules for it.\n// From then on the workspace decides, rule by rule, whether a matching action is blocked or only recorded, and may add entries\n// to the computer's own lists (protected branches, programs, allowed sites). It never sends a pattern: this module compiles the\n// workspace's choices with the same compiler `rules apply` uses, so what decides a block lives in one place.\n//\n// What a workspace cannot change: the protection of the hook's own settings (and the settings of the agents it guards), the\n// machine key policy, fail-closed handling of anything unparseable, and the computer's own opt-in extras (`allowed_roots`,\n// `protect_remote_database`). A rule set to Monitor is still covered by an allow clause, so the action is recorded and passes;\n// leaving it out would block the whole action type, because the verifier denies an action no clause covers.\n//\n// Loading is checked and atomic: the document must be well formed, name this computer, carry a matching digest and be newer\n// than the one in force; the merged policy must build; policy.json is written through a temp file and a rename, the previous\n// one kept. Disconnecting, or a workspace that stops managing this computer, restores the computer's own rules.\n\nimport { copyFileSync, existsSync, mkdirSync, readFileSync, renameSync, rmSync, writeFileSync } from \"node:fs\";\nimport { join } from \"node:path\";\nimport { createHash, randomBytes } from \"node:crypto\";\nimport { canonical } from \"@scopebond/policy-schema/canonical\";\nimport { GUARDRAIL_WRITE_PATTERN, ci } from \"./runtime.js\";\nimport { compile, defaultRules, loadRules, type RuleSet } from \"./rules.js\";\n\nexport const MANAGED_DOC_FILE = \"managed-rules.json\";\nexport const MANAGED_META_FILE = \"managed-meta.json\";\nexport const PREVIOUS_POLICY_FILE = \"policy.previous.json\";\n\nexport const MANAGED_RULE_IDS = [\"force-push-protected\", \"push-protected\", \"destructive-shell\", \"secret-read\", \"ci-config-write\", \"network-egress\"] as const;\nexport type ManagedRuleId = (typeof MANAGED_RULE_IDS)[number];\ntype ListKey = \"protected_branches\" | \"destructive_programs\" | \"allowed_hosts\";\nconst LIST_OF: Partial<Record<ManagedRuleId, ListKey>> = {\n \"force-push-protected\": \"protected_branches\", \"push-protected\": \"protected_branches\",\n \"destructive-shell\": \"destructive_programs\", \"network-egress\": \"allowed_hosts\",\n};\nconst LIST_PATTERN: Record<ListKey, RegExp> = {\n protected_branches: /^[A-Za-z0-9._/*-]{1,100}$/,\n destructive_programs: /^[a-z0-9][a-z0-9._-]{0,63}$/,\n allowed_hosts: /^(?=.{1,253}$)(\\*\\.)?([a-z0-9]([a-z0-9-]{0,61}[a-z0-9])?\\.)+[a-z]{2,63}$/,\n};\nconst HEX64 = /^[0-9a-f]{64}$/;\nconst OPAQUE = /^[A-Za-z0-9._:-]{1,128}$/;\n\nexport type ManagedRule = { mode: \"monitor\" | \"block\" } & Partial<Record<ListKey, string[]>>;\nexport interface ManagedDocument {\n type: \"scopebond:managed-rules\";\n version: 1;\n revision: number;\n export_id: string;\n environment_id: string;\n agent_id: string;\n installation_id: string;\n rules: Record<ManagedRuleId, ManagedRule>;\n rules_digest: string;\n}\n\nexport type RefusalReason = \"invalid_document\" | \"stale_revision\" | \"wrong_computer\" | \"unsupported\" | \"write_failed\";\nexport type Inspection = { ok: true; doc: ManagedDocument } | { ok: false; reason: RefusalReason; message: string };\n\nexport const digestRules = (rules: unknown): string => createHash(\"sha256\").update(canonical(rules)).digest(\"hex\");\n\nconst isObject = (v: unknown): v is Record<string, unknown> => typeof v === \"object\" && v !== null && !Array.isArray(v);\n\n/** Check a document from the workspace without touching disk. Pure. */\nexport function inspectManaged(raw: unknown, context: { installationId: string; currentRevision: number | null }): Inspection {\n const bad = (message: string): Inspection => ({ ok: false, reason: \"invalid_document\", message });\n if (!isObject(raw) || raw.type !== \"scopebond:managed-rules\") return bad(\"this is not a Scopebond workspace rules document\");\n if (raw.version !== 1) return { ok: false, reason: \"unsupported\", message: `unsupported rules document version ${String(raw.version)}` };\n const { revision, export_id: exportId, environment_id: environmentId, agent_id: agentId, installation_id: installationId, rules, rules_digest: rulesDigest } = raw;\n if (!Number.isInteger(revision) || (revision as number) < 1 || (revision as number) > 2_147_483_647\n || typeof exportId !== \"string\" || typeof environmentId !== \"string\" || !OPAQUE.test(environmentId)\n || typeof agentId !== \"string\" || !OPAQUE.test(agentId) || typeof installationId !== \"string\" || !OPAQUE.test(installationId)\n || typeof rulesDigest !== \"string\" || !HEX64.test(rulesDigest) || !isObject(rules)) {\n return bad(\"the rules document is incomplete or malformed\");\n }\n if (installationId !== context.installationId || exportId !== `rev-${revision}-${installationId}`) {\n return { ok: false, reason: \"wrong_computer\", message: \"these rules were issued for a different computer\" };\n }\n const keys = Object.keys(rules);\n if (keys.length !== MANAGED_RULE_IDS.length || !MANAGED_RULE_IDS.every((id) => keys.includes(id))) {\n return { ok: false, reason: \"unsupported\", message: \"the rules document names rules this version does not know; update Scopebond\" };\n }\n for (const id of MANAGED_RULE_IDS) {\n const rule = rules[id];\n if (!isObject(rule) || (rule.mode !== \"monitor\" && rule.mode !== \"block\")) return bad(`rule ${id} has no valid mode`);\n for (const key of Object.keys(rule)) {\n if (key === \"mode\") continue;\n const listKey = LIST_OF[id];\n if (key !== listKey) return bad(`rule ${id} has an unknown setting`);\n const list = rule[key];\n if (!Array.isArray(list) || list.length > 50 || !list.every((v) => typeof v === \"string\" && LIST_PATTERN[listKey].test(v) && !v.includes(\"..\"))) {\n return bad(`rule ${id} has an invalid list`);\n }\n }\n }\n const network = rules[\"network-egress\"] as ManagedRule;\n if (network.mode === \"block\" && !(network.allowed_hosts?.length)) return bad(\"network blocking needs at least one allowed site\");\n if (digestRules(rules) !== rulesDigest) return bad(\"the rules do not match their digest; the document was changed or damaged\");\n if (context.currentRevision !== null && (revision as number) <= context.currentRevision) {\n return { ok: false, reason: \"stale_revision\", message: `version ${String(revision)} is not newer than the version in force (${context.currentRevision})` };\n }\n return { ok: true, doc: raw as unknown as ManagedDocument };\n}\n\nconst union = (a: readonly string[], b: readonly string[] | undefined): string[] => [...new Set([...a, ...(b ?? [])])];\nconst branchGlob = (b: string): string => (b.endsWith(\"/*\") ? `${b.slice(0, -1)}**` : b);\nconst hostPattern = (hosts: readonly string[]): string =>\n `^(?:${hosts.map((h) => (h.startsWith(\"*.\") ? `(?:[^.]+\\\\.)+${ci(h.slice(2).replace(/\\./g, \"\\\\.\"))}` : ci(h.replace(/\\./g, \"\\\\.\")))).join(\"|\")})$`;\n\ntype Clause = Record<string, unknown> & { id: string };\n\n/** The policy this computer enforces under workspace rules: its own compiled rules, adjusted rule by rule. Pure. */\nexport function compileManaged(local: RuleSet, doc: ManagedDocument, agentKid: string): Record<string, unknown> {\n const r = doc.rules;\n const rules: RuleSet = {\n ...local,\n protected_branches: union(local.protected_branches, [...(r[\"push-protected\"].protected_branches ?? []), ...(r[\"force-push-protected\"].protected_branches ?? [])]),\n destructive_programs: union(local.destructive_programs, r[\"destructive-shell\"].destructive_programs),\n };\n const policy = compile(rules, agentKid) as { clauses: Clause[] } & Record<string, unknown>;\n const allowAll = (clause: Clause): Clause => { const { param_bounds: _dropped, ...rest } = clause; return { ...rest, mode: \"enforce\", description: `${String(clause.description ?? \"\").split(\".\")[0]}. Recorded, not blocked: set by your workspace.` }; };\n const clauses: Clause[] = [];\n for (const clause of policy.clauses) {\n if (clause.id === \"protect-branches\" && r[\"push-protected\"].mode === \"monitor\") {\n clauses.push(allowAll(clause));\n if (r[\"force-push-protected\"].mode === \"block\") {\n clauses.push({ id: \"protect-branch-history\", type: \"force_push_guard\", mode: \"enforce\", protected_refs: rules.protected_branches.map(branchGlob),\n description: \"Deny force-pushes, deletions and mirror pushes that reach a protected branch; ordinary pushes are allowed. Set by your workspace.\" });\n }\n } else if (clause.id === \"safe-shell\" && r[\"destructive-shell\"].mode === \"monitor\") clauses.push(allowAll(clause));\n else if (clause.id === \"protect-read\" && r[\"secret-read\"].mode === \"monitor\") clauses.push(allowAll(clause));\n else if (clause.id === \"protect-write\" && r[\"ci-config-write\"].mode === \"monitor\") {\n clauses.push({ ...clause, param_bounds: { path: { pattern: GUARDRAIL_WRITE_PATTERN } },\n description: \"Allow workspace writes, but never to Scopebond's own settings or the agents' hook settings (always on). Other protected files are recorded, not blocked: set by your workspace.\" });\n } else clauses.push(clause);\n }\n if (r[\"network-egress\"].mode === \"block\") {\n const keysAt = clauses.findIndex((c) => c.id === \"keys\");\n clauses.splice(keysAt < 0 ? clauses.length : keysAt, 0, {\n id: \"allowed-sites\", type: \"action_allowlist\", mode: \"enforce\", action_types: [\"net.fetch\"],\n param_bounds: { host: { pattern: hostPattern(r[\"network-egress\"].allowed_hosts ?? []) } },\n description: `Deny web requests to sites other than ${r[\"network-egress\"].allowed_hosts!.join(\", \")}. Set by your workspace.`,\n });\n }\n return { ...policy, policy_id: \"coding-agent\", version: doc.revision, clauses };\n}\n\n// --- on disk ---------------------------------------------------------------------------------------------------------------------\n\nexport interface ManagedMeta {\n revision: number | null;\n rules_digest: string | null;\n export_id: string | null;\n etag: string | null;\n checked_at: string | null;\n last_ack: { revision: number; result: \"loaded\" | \"rejected\"; at: string } | null;\n last_error: string | null;\n}\nconst EMPTY_META: ManagedMeta = { revision: null, rules_digest: null, export_id: null, etag: null, checked_at: null, last_ack: null, last_error: null };\n\nexport function readMeta(dir: string): ManagedMeta {\n try { return { ...EMPTY_META, ...(JSON.parse(readFileSync(join(dir, MANAGED_META_FILE), \"utf8\")) as Partial<ManagedMeta>) }; } catch { return { ...EMPTY_META }; }\n}\nexport function writeMeta(dir: string, meta: ManagedMeta): void { writeAtomic(join(dir, MANAGED_META_FILE), `${JSON.stringify(meta, null, 2)}\\n`); }\n\n/** Whether a workspace currently manages the rules on this computer. */\nexport const isManaged = (dir: string): boolean => existsSync(join(dir, MANAGED_DOC_FILE));\n\nexport function writeAtomic(file: string, text: string): void {\n const temp = `${file}.${randomBytes(6).toString(\"hex\")}.tmp`;\n try { writeFileSync(temp, text, { mode: 0o600 }); renameSync(temp, file); }\n catch (error) { try { rmSync(temp, { force: true }); } catch { /* nothing to remove */ } throw error; }\n}\n\n/** Install a checked document: write the merged policy (previous kept) and the document. `policyBuilds` is the caller's check\n * that the gateway can load the result. */\nexport function installManaged(dir: string, doc: ManagedDocument, agentKid: string, policyBuilds: (policy: Record<string, unknown>) => boolean): { ok: true } | { ok: false; reason: RefusalReason; message: string } {\n const local = loadRules(dir) ?? defaultRules();\n const policy = compileManaged(local, doc, agentKid);\n if (!policyBuilds(policy)) return { ok: false, reason: \"unsupported\", message: \"this version of Scopebond cannot load the workspace rules; update it\" };\n try {\n mkdirSync(dir, { recursive: true });\n const policyPath = join(dir, \"policy.json\");\n if (existsSync(policyPath)) copyFileSync(policyPath, join(dir, PREVIOUS_POLICY_FILE));\n writeAtomic(policyPath, `${JSON.stringify(policy, null, 2)}\\n`);\n writeAtomic(join(dir, MANAGED_DOC_FILE), `${JSON.stringify(doc, null, 2)}\\n`);\n return { ok: true };\n } catch (error) {\n return { ok: false, reason: \"write_failed\", message: `could not write the rules (${(error as Error).name})` };\n }\n}\n\n/** Go back to this computer's own rules: recompile policy.json from rules.json and forget the workspace document. */\nexport function restoreLocal(dir: string, agentKid: string): void {\n const local = loadRules(dir) ?? defaultRules();\n const policyPath = join(dir, \"policy.json\");\n if (existsSync(policyPath)) copyFileSync(policyPath, join(dir, PREVIOUS_POLICY_FILE));\n writeAtomic(policyPath, `${JSON.stringify(compile(local, agentKid), null, 2)}\\n`);\n rmSync(join(dir, MANAGED_DOC_FILE), { force: true });\n}\n"]}
@@ -0,0 +1,45 @@
1
+ import { type RefusalReason } from "./managed.js";
2
+ export declare const SYNC_INTERVAL_MS: number;
3
+ /** How long a tool call may spend on the five-minute check, at most (`SCOPEBOND_POLICY_SYNC_MS` overrides). */
4
+ export declare const INLINE_BUDGET_MS = 1500;
5
+ export type SyncOutcome = {
6
+ state: "not_connected";
7
+ } | {
8
+ state: "own_rules";
9
+ changed: boolean;
10
+ } | {
11
+ state: "unchanged";
12
+ revision: number;
13
+ } | {
14
+ state: "applied";
15
+ revision: number;
16
+ } | {
17
+ state: "refused";
18
+ revision: number | null;
19
+ reason: RefusalReason;
20
+ message: string;
21
+ } | {
22
+ state: "disconnected";
23
+ } | {
24
+ state: "unavailable";
25
+ message: string;
26
+ };
27
+ export interface SyncOptions {
28
+ agentKid: string;
29
+ hookVersion: string;
30
+ policyBuilds: (policy: Record<string, unknown>) => boolean;
31
+ fetchImpl?: typeof fetch;
32
+ now?: () => Date;
33
+ /** Recompute a project's trust pin after its policy.json changed (the same step `rules apply` takes). */
34
+ afterPolicyWrite?: (dir: string) => void;
35
+ /** Per-request timeout; defaults to five seconds. */
36
+ timeoutMs?: number;
37
+ }
38
+ export declare function syncPolicy(dir: string, options: SyncOptions): Promise<SyncOutcome>;
39
+ /** Called on every tool call: when this computer is connected and has not checked in five minutes, run the check, capped at
40
+ * `budgetMs`. Resolves `null` when no check was due, another one is running, or the cap was reached first (the check then
41
+ * finishes if the process lives long enough, else runs again on a later call). Never throws. `makeOptions` is called only when a
42
+ * check is due, so an ordinary call pays nothing but two file reads. */
43
+ export declare function syncIfDue(dir: string, makeOptions: () => SyncOptions, budgetMs?: number, now?: number): Promise<SyncOutcome | null>;
44
+ export declare function releaseSyncLock(dir: string): void;
45
+ //# sourceMappingURL=policy-sync.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"policy-sync.d.ts","sourceRoot":"","sources":["../src/policy-sync.ts"],"names":[],"mappings":"AAmBA,OAAO,EAC2F,KAAK,aAAa,EACnH,MAAM,cAAc,CAAC;AAEtB,eAAO,MAAM,gBAAgB,QAAgB,CAAC;AAI9C,+GAA+G;AAC/G,eAAO,MAAM,gBAAgB,OAAQ,CAAC;AAGtC,MAAM,MAAM,WAAW,GACnB;IAAE,KAAK,EAAE,eAAe,CAAA;CAAE,GAC1B;IAAE,KAAK,EAAE,WAAW,CAAC;IAAC,OAAO,EAAE,OAAO,CAAA;CAAE,GACxC;IAAE,KAAK,EAAE,WAAW,CAAC;IAAC,QAAQ,EAAE,MAAM,CAAA;CAAE,GACxC;IAAE,KAAK,EAAE,SAAS,CAAC;IAAC,QAAQ,EAAE,MAAM,CAAA;CAAE,GACtC;IAAE,KAAK,EAAE,SAAS,CAAC;IAAC,QAAQ,EAAE,MAAM,GAAG,IAAI,CAAC;IAAC,MAAM,EAAE,aAAa,CAAC;IAAC,OAAO,EAAE,MAAM,CAAA;CAAE,GACrF;IAAE,KAAK,EAAE,cAAc,CAAA;CAAE,GACzB;IAAE,KAAK,EAAE,aAAa,CAAC;IAAC,OAAO,EAAE,MAAM,CAAA;CAAE,CAAC;AAE9C,MAAM,WAAW,WAAW;IAC1B,QAAQ,EAAE,MAAM,CAAC;IACjB,WAAW,EAAE,MAAM,CAAC;IACpB,YAAY,EAAE,CAAC,MAAM,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,KAAK,OAAO,CAAC;IAC3D,SAAS,CAAC,EAAE,OAAO,KAAK,CAAC;IACzB,GAAG,CAAC,EAAE,MAAM,IAAI,CAAC;IACjB,yGAAyG;IACzG,gBAAgB,CAAC,EAAE,CAAC,GAAG,EAAE,MAAM,KAAK,IAAI,CAAC;IACzC,qDAAqD;IACrD,SAAS,CAAC,EAAE,MAAM,CAAC;CACpB;AAED,wBAAsB,UAAU,CAAC,GAAG,EAAE,MAAM,EAAE,OAAO,EAAE,WAAW,GAAG,OAAO,CAAC,WAAW,CAAC,CA4FxF;AAED;;;yEAGyE;AACzE,wBAAsB,SAAS,CAAC,GAAG,EAAE,MAAM,EAAE,WAAW,EAAE,MAAM,WAAW,EAAE,QAAQ,SAAmE,EAAE,GAAG,SAAa,GAAG,OAAO,CAAC,WAAW,GAAG,IAAI,CAAC,CAyBvM;AAED,wBAAgB,eAAe,CAAC,GAAG,EAAE,MAAM,GAAG,IAAI,CAEjD"}
@@ -0,0 +1,171 @@
1
+ // Keep a connected computer's rules in step with its Scopebond Cloud workspace.
2
+ //
3
+ // `syncPolicy` asks the workspace for this computer's rules (`GET /v1/policy`), installs a newer version when there is one, and
4
+ // tells the workspace exactly what it loaded or why it could not (`POST /v1/policy/ack`). A tool call checks two small files;
5
+ // at most every five minutes it also runs the check (`syncIfDue`), in parallel with the delivery of its activity record and
6
+ // capped at about one and a half seconds, so a change made in the workspace applies within a few minutes of the agent's next
7
+ // action. It runs in the hook's own process on purpose: a detached helper process inherits the hook's output pipe on Windows,
8
+ // which makes the coding agent wait for the helper. Any failure or timeout keeps the rules already in force; an interrupted
9
+ // write cannot leave a half-written policy, because every write is a temp file and a rename.
10
+ //
11
+ // Answers from the workspace:
12
+ // 204 the workspace does not set rules for this computer: use (or go back to) its own rules
13
+ // 304 nothing changed since the last check
14
+ // 200 a rules document: checked, installed if newer, confirmed
15
+ // 401 the connection is no longer valid (revoked, expired, removed): go back to this computer's own rules
16
+ import { closeSync, existsSync, openSync, rmSync, statSync, writeSync } from "node:fs";
17
+ import { join } from "node:path";
18
+ import { connectionPath, loadConnection } from "./cloud.js";
19
+ import { inspectManaged, installManaged, isManaged, readMeta, restoreLocal, writeMeta, } from "./managed.js";
20
+ export const SYNC_INTERVAL_MS = 5 * 60 * 1000;
21
+ const LOCK_FILE = "managed-sync.lock";
22
+ const LOCK_STALE_MS = 2 * 60 * 1000;
23
+ const REQUEST_TIMEOUT_MS = 5_000;
24
+ /** How long a tool call may spend on the five-minute check, at most (`SCOPEBOND_POLICY_SYNC_MS` overrides). */
25
+ export const INLINE_BUDGET_MS = 1_500;
26
+ const UNMANAGED_DIGEST = "0".repeat(64);
27
+ export async function syncPolicy(dir, options) {
28
+ const connection = loadConnection(dir);
29
+ if (!connection)
30
+ return { state: "not_connected" };
31
+ const fetchImpl = options.fetchImpl ?? fetch;
32
+ const now = (options.now ?? (() => new Date()))();
33
+ const installationId = connection.installation_id ?? connection.gateway_id;
34
+ const base = connection.url.replace(/\/+$/, "");
35
+ const auth = { authorization: `Bearer ${connection.credential}` };
36
+ const meta = readMeta(dir);
37
+ const save = (patch) => writeMeta(dir, { ...meta, ...patch, checked_at: now.toISOString() });
38
+ const ack = async (body) => {
39
+ try {
40
+ const res = await fetchImpl(`${base}/v1/policy/ack`, {
41
+ method: "POST", headers: { ...auth, "content-type": "application/json" }, redirect: "error", signal: AbortSignal.timeout(options.timeoutMs ?? REQUEST_TIMEOUT_MS),
42
+ body: JSON.stringify({ ...body, reason: body.reason ?? null, hook_version: options.hookVersion }),
43
+ });
44
+ return res.ok ? { revision: body.revision, result: body.result, at: now.toISOString() } : meta.last_ack;
45
+ }
46
+ catch {
47
+ return meta.last_ack;
48
+ }
49
+ };
50
+ const backToOwnRules = () => {
51
+ if (!isManaged(dir))
52
+ return false;
53
+ restoreLocal(dir, options.agentKid);
54
+ options.afterPolicyWrite?.(dir);
55
+ return true;
56
+ };
57
+ let res;
58
+ try {
59
+ res = await fetchImpl(`${base}/v1/policy`, {
60
+ headers: { ...auth, ...(isManaged(dir) && meta.etag ? { "if-none-match": meta.etag } : {}) },
61
+ redirect: "error", signal: AbortSignal.timeout(options.timeoutMs ?? REQUEST_TIMEOUT_MS),
62
+ });
63
+ }
64
+ catch (error) {
65
+ save({ last_error: `could not reach the workspace (${error.name}); the rules in force stay` });
66
+ return { state: "unavailable", message: "could not reach the workspace" };
67
+ }
68
+ if (res.status === 401) {
69
+ backToOwnRules();
70
+ save({ revision: null, rules_digest: null, export_id: null, etag: null, last_error: "the workspace connection is no longer valid; this computer uses its own rules" });
71
+ return { state: "disconnected" };
72
+ }
73
+ if (res.status === 204) {
74
+ const changed = backToOwnRules();
75
+ const last = meta.last_ack;
76
+ const confirmed = changed || !last || last.revision !== 0
77
+ ? await ack({ export_id: `rev-0-${installationId}`, revision: 0, rules_digest: UNMANAGED_DIGEST, result: "loaded" }) : last;
78
+ save({ revision: null, rules_digest: null, export_id: null, etag: null, last_ack: confirmed, last_error: null });
79
+ return { state: "own_rules", changed };
80
+ }
81
+ if (res.status === 304 && isManaged(dir) && meta.revision !== null) {
82
+ const confirmed = meta.last_ack?.revision === meta.revision && meta.last_ack.result === "loaded" ? meta.last_ack
83
+ : await ack({ export_id: meta.export_id, revision: meta.revision, rules_digest: meta.rules_digest, result: "loaded" });
84
+ save({ last_ack: confirmed, last_error: null });
85
+ return { state: "unchanged", revision: meta.revision };
86
+ }
87
+ if (res.status !== 200) {
88
+ save({ last_error: `the workspace answered ${res.status}; the rules in force stay` });
89
+ return { state: "unavailable", message: `the workspace answered ${res.status}` };
90
+ }
91
+ let raw;
92
+ try {
93
+ raw = await res.json();
94
+ }
95
+ catch {
96
+ raw = null;
97
+ }
98
+ const r = (raw && typeof raw === "object" ? raw : {});
99
+ const etag = res.headers.get("etag");
100
+ // The same version already in force: confirm it again if the last confirmation did not land.
101
+ if (isManaged(dir) && r.revision === meta.revision && r.rules_digest === meta.rules_digest && meta.revision !== null) {
102
+ const confirmed = meta.last_ack?.revision === meta.revision ? meta.last_ack
103
+ : await ack({ export_id: meta.export_id, revision: meta.revision, rules_digest: meta.rules_digest, result: "loaded" });
104
+ save({ etag, last_ack: confirmed, last_error: null });
105
+ return { state: "unchanged", revision: meta.revision };
106
+ }
107
+ const inspected = inspectManaged(raw, { installationId, currentRevision: isManaged(dir) ? meta.revision : null });
108
+ const echo = typeof r.export_id === "string" && Number.isInteger(r.revision) && typeof r.rules_digest === "string" && /^[0-9a-f]{64}$/.test(r.rules_digest)
109
+ ? { export_id: r.export_id, revision: r.revision, rules_digest: r.rules_digest } : null;
110
+ if (!inspected.ok) {
111
+ const confirmed = echo ? await ack({ ...echo, result: "rejected", reason: inspected.reason }) : meta.last_ack;
112
+ save({ last_ack: confirmed, last_error: inspected.message });
113
+ return { state: "refused", revision: echo?.revision ?? null, reason: inspected.reason, message: inspected.message };
114
+ }
115
+ const doc = inspected.doc;
116
+ const installed = installManaged(dir, doc, options.agentKid, options.policyBuilds);
117
+ if (!installed.ok) {
118
+ const confirmed = await ack({ export_id: doc.export_id, revision: doc.revision, rules_digest: doc.rules_digest, result: "rejected", reason: installed.reason });
119
+ save({ last_ack: confirmed, last_error: installed.message });
120
+ return { state: "refused", revision: doc.revision, reason: installed.reason, message: installed.message };
121
+ }
122
+ options.afterPolicyWrite?.(dir);
123
+ const confirmed = await ack({ export_id: doc.export_id, revision: doc.revision, rules_digest: doc.rules_digest, result: "loaded" });
124
+ save({ revision: doc.revision, rules_digest: doc.rules_digest, export_id: doc.export_id, etag, last_ack: confirmed, last_error: null });
125
+ return { state: "applied", revision: doc.revision };
126
+ }
127
+ /** Called on every tool call: when this computer is connected and has not checked in five minutes, run the check, capped at
128
+ * `budgetMs`. Resolves `null` when no check was due, another one is running, or the cap was reached first (the check then
129
+ * finishes if the process lives long enough, else runs again on a later call). Never throws. `makeOptions` is called only when a
130
+ * check is due, so an ordinary call pays nothing but two file reads. */
131
+ export async function syncIfDue(dir, makeOptions, budgetMs = Number(process.env.SCOPEBOND_POLICY_SYNC_MS ?? INLINE_BUDGET_MS), now = Date.now()) {
132
+ try {
133
+ if (process.env.SCOPEBOND_POLICY_SYNC === "off")
134
+ return null;
135
+ if (!existsSync(connectionPath(dir)))
136
+ return null;
137
+ const checked = readMeta(dir).checked_at;
138
+ if (checked && now - Date.parse(checked) < SYNC_INTERVAL_MS)
139
+ return null;
140
+ const lock = join(dir, LOCK_FILE);
141
+ if (existsSync(lock)) {
142
+ if (now - statSync(lock).mtimeMs < LOCK_STALE_MS)
143
+ return null;
144
+ rmSync(lock, { force: true });
145
+ }
146
+ const fd = openSync(lock, "wx");
147
+ writeSync(fd, String(process.pid));
148
+ closeSync(fd);
149
+ const budget = Number.isFinite(budgetMs) && budgetMs > 0 ? budgetMs : INLINE_BUDGET_MS;
150
+ const work = Promise.resolve()
151
+ .then(() => syncPolicy(dir, { ...makeOptions(), timeoutMs: budget }))
152
+ .catch(() => ({ state: "unavailable", message: "the check failed" }))
153
+ .finally(() => releaseSyncLock(dir));
154
+ let timer;
155
+ const capped = new Promise((resolve) => { timer = setTimeout(() => resolve(null), budget); });
156
+ const outcome = await Promise.race([work, capped]);
157
+ if (timer)
158
+ clearTimeout(timer);
159
+ return outcome;
160
+ }
161
+ catch {
162
+ return null;
163
+ }
164
+ }
165
+ export function releaseSyncLock(dir) {
166
+ try {
167
+ rmSync(join(dir, LOCK_FILE), { force: true });
168
+ }
169
+ catch { /* expires on its own */ }
170
+ }
171
+ //# sourceMappingURL=policy-sync.js.map