narai-primitives 2.1.3 → 2.2.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +16 -4
- package/dist/config/load.d.ts.map +1 -1
- package/dist/config/load.js +12 -1
- package/dist/config/load.js.map +1 -1
- package/dist/connectors/confluence/index.d.ts +3 -1
- package/dist/connectors/confluence/index.d.ts.map +1 -1
- package/dist/connectors/confluence/index.js +246 -31
- package/dist/connectors/confluence/index.js.map +1 -1
- package/dist/connectors/confluence/lib/confluence_client.d.ts +50 -30
- package/dist/connectors/confluence/lib/confluence_client.d.ts.map +1 -1
- package/dist/connectors/confluence/lib/confluence_client.js +69 -225
- package/dist/connectors/confluence/lib/confluence_client.js.map +1 -1
- package/dist/connectors/db/connector.d.ts.map +1 -1
- package/dist/connectors/db/connector.js +12 -6
- package/dist/connectors/db/connector.js.map +1 -1
- package/dist/connectors/db/dispatcher.d.ts +3 -2
- package/dist/connectors/db/dispatcher.d.ts.map +1 -1
- package/dist/connectors/db/dispatcher.js +136 -40
- package/dist/connectors/db/dispatcher.js.map +1 -1
- package/dist/connectors/db/index.d.ts +9 -0
- package/dist/connectors/db/index.d.ts.map +1 -1
- package/dist/connectors/db/index.js +9 -0
- package/dist/connectors/db/index.js.map +1 -1
- package/dist/connectors/db/lib/audit.d.ts.map +1 -1
- package/dist/connectors/db/lib/audit.js +46 -4
- package/dist/connectors/db/lib/audit.js.map +1 -1
- package/dist/connectors/db/lib/drivers/dynamodb.d.ts.map +1 -1
- package/dist/connectors/db/lib/drivers/dynamodb.js +24 -4
- package/dist/connectors/db/lib/drivers/dynamodb.js.map +1 -1
- package/dist/connectors/db/lib/drivers/mysql.js +1 -1
- package/dist/connectors/db/lib/drivers/mysql.js.map +1 -1
- package/dist/connectors/db/lib/drivers/postgresql.js +1 -1
- package/dist/connectors/db/lib/drivers/postgresql.js.map +1 -1
- package/dist/connectors/db/lib/drivers/sqlite.d.ts.map +1 -1
- package/dist/connectors/db/lib/drivers/sqlite.js +9 -5
- package/dist/connectors/db/lib/drivers/sqlite.js.map +1 -1
- package/dist/connectors/db/lib/drivers/sqlserver.d.ts.map +1 -1
- package/dist/connectors/db/lib/drivers/sqlserver.js +66 -32
- package/dist/connectors/db/lib/drivers/sqlserver.js.map +1 -1
- package/dist/connectors/db/lib/environments.d.ts +16 -0
- package/dist/connectors/db/lib/environments.d.ts.map +1 -1
- package/dist/connectors/db/lib/environments.js +16 -0
- package/dist/connectors/db/lib/environments.js.map +1 -1
- package/dist/connectors/db/lib/grant-store.d.ts +70 -0
- package/dist/connectors/db/lib/grant-store.d.ts.map +1 -0
- package/dist/connectors/db/lib/grant-store.js +149 -0
- package/dist/connectors/db/lib/grant-store.js.map +1 -0
- package/dist/connectors/db/lib/plugin_config.d.ts +2 -0
- package/dist/connectors/db/lib/plugin_config.d.ts.map +1 -1
- package/dist/connectors/db/lib/plugin_config.js +23 -2
- package/dist/connectors/db/lib/plugin_config.js.map +1 -1
- package/dist/connectors/db/lib/policy.d.ts +50 -14
- package/dist/connectors/db/lib/policy.d.ts.map +1 -1
- package/dist/connectors/db/lib/policy.js +295 -73
- package/dist/connectors/db/lib/policy.js.map +1 -1
- package/dist/connectors/github/actions/_fields.d.ts +18 -0
- package/dist/connectors/github/actions/_fields.d.ts.map +1 -0
- package/dist/connectors/github/actions/_fields.js +29 -0
- package/dist/connectors/github/actions/_fields.js.map +1 -0
- package/dist/connectors/github/actions/_pagination.d.ts +12 -0
- package/dist/connectors/github/actions/_pagination.d.ts.map +1 -0
- package/dist/connectors/github/actions/_pagination.js +26 -0
- package/dist/connectors/github/actions/_pagination.js.map +1 -0
- package/dist/connectors/github/actions/_types.d.ts +14 -0
- package/dist/connectors/github/actions/_types.d.ts.map +1 -0
- package/dist/connectors/github/actions/_types.js +2 -0
- package/dist/connectors/github/actions/_types.js.map +1 -0
- package/dist/connectors/github/actions/comments.d.ts +3 -0
- package/dist/connectors/github/actions/comments.d.ts.map +1 -0
- package/dist/connectors/github/actions/comments.js +166 -0
- package/dist/connectors/github/actions/comments.js.map +1 -0
- package/dist/connectors/github/actions/issues.d.ts +3 -0
- package/dist/connectors/github/actions/issues.d.ts.map +1 -0
- package/dist/connectors/github/actions/issues.js +129 -0
- package/dist/connectors/github/actions/issues.js.map +1 -0
- package/dist/connectors/github/actions/pulls.d.ts +3 -0
- package/dist/connectors/github/actions/pulls.d.ts.map +1 -0
- package/dist/connectors/github/actions/pulls.js +182 -0
- package/dist/connectors/github/actions/pulls.js.map +1 -0
- package/dist/connectors/github/actions/reads.d.ts +3 -0
- package/dist/connectors/github/actions/reads.d.ts.map +1 -0
- package/dist/connectors/github/actions/reads.js +349 -0
- package/dist/connectors/github/actions/reads.js.map +1 -0
- package/dist/connectors/github/actions/releases.d.ts +3 -0
- package/dist/connectors/github/actions/releases.d.ts.map +1 -0
- package/dist/connectors/github/actions/releases.js +124 -0
- package/dist/connectors/github/actions/releases.js.map +1 -0
- package/dist/connectors/github/actions/workflows.d.ts +3 -0
- package/dist/connectors/github/actions/workflows.d.ts.map +1 -0
- package/dist/connectors/github/actions/workflows.js +224 -0
- package/dist/connectors/github/actions/workflows.js.map +1 -0
- package/dist/connectors/github/index.d.ts +13 -1
- package/dist/connectors/github/index.d.ts.map +1 -1
- package/dist/connectors/github/index.js +33 -396
- package/dist/connectors/github/index.js.map +1 -1
- package/dist/connectors/github/lib/github_client.d.ts +242 -29
- package/dist/connectors/github/lib/github_client.d.ts.map +1 -1
- package/dist/connectors/github/lib/github_client.js +202 -256
- package/dist/connectors/github/lib/github_client.js.map +1 -1
- package/dist/connectors/github/lib/github_config.d.ts +10 -0
- package/dist/connectors/github/lib/github_config.d.ts.map +1 -0
- package/dist/connectors/github/lib/github_config.js +79 -0
- package/dist/connectors/github/lib/github_config.js.map +1 -0
- package/dist/connectors/gitlab/actions/_fields.d.ts +20 -0
- package/dist/connectors/gitlab/actions/_fields.d.ts.map +1 -0
- package/dist/connectors/gitlab/actions/_fields.js +44 -0
- package/dist/connectors/gitlab/actions/_fields.js.map +1 -0
- package/dist/connectors/gitlab/actions/_pagination.d.ts +19 -0
- package/dist/connectors/gitlab/actions/_pagination.d.ts.map +1 -0
- package/dist/connectors/gitlab/actions/_pagination.js +33 -0
- package/dist/connectors/gitlab/actions/_pagination.js.map +1 -0
- package/dist/connectors/gitlab/actions/_types.d.ts +12 -0
- package/dist/connectors/gitlab/actions/_types.d.ts.map +1 -0
- package/dist/connectors/gitlab/actions/_types.js +2 -0
- package/dist/connectors/gitlab/actions/_types.js.map +1 -0
- package/dist/connectors/gitlab/actions/issues.d.ts +3 -0
- package/dist/connectors/gitlab/actions/issues.d.ts.map +1 -0
- package/dist/connectors/gitlab/actions/issues.js +119 -0
- package/dist/connectors/gitlab/actions/issues.js.map +1 -0
- package/dist/connectors/gitlab/actions/merges.d.ts +3 -0
- package/dist/connectors/gitlab/actions/merges.d.ts.map +1 -0
- package/dist/connectors/gitlab/actions/merges.js +198 -0
- package/dist/connectors/gitlab/actions/merges.js.map +1 -0
- package/dist/connectors/gitlab/actions/notes.d.ts +3 -0
- package/dist/connectors/gitlab/actions/notes.d.ts.map +1 -0
- package/dist/connectors/gitlab/actions/notes.js +145 -0
- package/dist/connectors/gitlab/actions/notes.js.map +1 -0
- package/dist/connectors/gitlab/actions/pipelines.d.ts +3 -0
- package/dist/connectors/gitlab/actions/pipelines.d.ts.map +1 -0
- package/dist/connectors/gitlab/actions/pipelines.js +136 -0
- package/dist/connectors/gitlab/actions/pipelines.js.map +1 -0
- package/dist/connectors/gitlab/actions/reads.d.ts +3 -0
- package/dist/connectors/gitlab/actions/reads.d.ts.map +1 -0
- package/dist/connectors/gitlab/actions/reads.js +422 -0
- package/dist/connectors/gitlab/actions/reads.js.map +1 -0
- package/dist/connectors/gitlab/actions/releases.d.ts +3 -0
- package/dist/connectors/gitlab/actions/releases.d.ts.map +1 -0
- package/dist/connectors/gitlab/actions/releases.js +99 -0
- package/dist/connectors/gitlab/actions/releases.js.map +1 -0
- package/dist/connectors/gitlab/cli.d.ts +3 -0
- package/dist/connectors/gitlab/cli.d.ts.map +1 -0
- package/dist/connectors/gitlab/cli.js +24 -0
- package/dist/connectors/gitlab/cli.js.map +1 -0
- package/dist/connectors/gitlab/index.d.ts +29 -0
- package/dist/connectors/gitlab/index.d.ts.map +1 -0
- package/dist/connectors/gitlab/index.js +95 -0
- package/dist/connectors/gitlab/index.js.map +1 -0
- package/dist/connectors/gitlab/lib/gitlab_client.d.ts +306 -0
- package/dist/connectors/gitlab/lib/gitlab_client.d.ts.map +1 -0
- package/dist/connectors/gitlab/lib/gitlab_client.js +249 -0
- package/dist/connectors/gitlab/lib/gitlab_client.js.map +1 -0
- package/dist/connectors/gitlab/lib/gitlab_config.d.ts +11 -0
- package/dist/connectors/gitlab/lib/gitlab_config.d.ts.map +1 -0
- package/dist/connectors/gitlab/lib/gitlab_config.js +115 -0
- package/dist/connectors/gitlab/lib/gitlab_config.js.map +1 -0
- package/dist/connectors/jira/index.d.ts +3 -1
- package/dist/connectors/jira/index.d.ts.map +1 -1
- package/dist/connectors/jira/index.js +299 -41
- package/dist/connectors/jira/index.js.map +1 -1
- package/dist/connectors/jira/lib/jira_client.d.ts +56 -41
- package/dist/connectors/jira/lib/jira_client.d.ts.map +1 -1
- package/dist/connectors/jira/lib/jira_client.js +71 -248
- package/dist/connectors/jira/lib/jira_client.js.map +1 -1
- package/dist/connectors/linear/cli.d.ts +3 -0
- package/dist/connectors/linear/cli.d.ts.map +1 -0
- package/dist/connectors/linear/cli.js +22 -0
- package/dist/connectors/linear/cli.js.map +1 -0
- package/dist/connectors/linear/index.d.ts +27 -0
- package/dist/connectors/linear/index.d.ts.map +1 -0
- package/dist/connectors/linear/index.js +496 -0
- package/dist/connectors/linear/index.js.map +1 -0
- package/dist/connectors/linear/lib/linear_client.d.ts +249 -0
- package/dist/connectors/linear/lib/linear_client.d.ts.map +1 -0
- package/dist/connectors/linear/lib/linear_client.js +154 -0
- package/dist/connectors/linear/lib/linear_client.js.map +1 -0
- package/dist/connectors/linear/lib/queries.d.ts +15 -0
- package/dist/connectors/linear/lib/queries.d.ts.map +1 -0
- package/dist/connectors/linear/lib/queries.js +188 -0
- package/dist/connectors/linear/lib/queries.js.map +1 -0
- package/dist/connectors/notion/index.d.ts +2 -1
- package/dist/connectors/notion/index.d.ts.map +1 -1
- package/dist/connectors/notion/index.js +213 -28
- package/dist/connectors/notion/index.js.map +1 -1
- package/dist/connectors/notion/lib/markdown_to_blocks.d.ts +21 -0
- package/dist/connectors/notion/lib/markdown_to_blocks.d.ts.map +1 -0
- package/dist/connectors/notion/lib/markdown_to_blocks.js +102 -0
- package/dist/connectors/notion/lib/markdown_to_blocks.js.map +1 -0
- package/dist/connectors/notion/lib/notion_blocks.d.ts +34 -0
- package/dist/connectors/notion/lib/notion_blocks.d.ts.map +1 -0
- package/dist/connectors/notion/lib/notion_blocks.js +87 -0
- package/dist/connectors/notion/lib/notion_blocks.js.map +1 -0
- package/dist/connectors/notion/lib/notion_client.d.ts +35 -25
- package/dist/connectors/notion/lib/notion_client.d.ts.map +1 -1
- package/dist/connectors/notion/lib/notion_client.js +63 -185
- package/dist/connectors/notion/lib/notion_client.js.map +1 -1
- package/dist/hub/index.d.ts.map +1 -1
- package/dist/hub/index.js +23 -3
- package/dist/hub/index.js.map +1 -1
- package/dist/toolkit/agent_resolver.d.ts +14 -4
- package/dist/toolkit/agent_resolver.d.ts.map +1 -1
- package/dist/toolkit/agent_resolver.js +38 -6
- package/dist/toolkit/agent_resolver.js.map +1 -1
- package/dist/toolkit/atlassian/adf_validator.d.ts +45 -0
- package/dist/toolkit/atlassian/adf_validator.d.ts.map +1 -0
- package/dist/toolkit/atlassian/adf_validator.js +83 -0
- package/dist/toolkit/atlassian/adf_validator.js.map +1 -0
- package/dist/toolkit/atlassian/index.d.ts +5 -0
- package/dist/toolkit/atlassian/index.d.ts.map +1 -0
- package/dist/toolkit/atlassian/index.js +5 -0
- package/dist/toolkit/atlassian/index.js.map +1 -0
- package/dist/toolkit/audit/writer.d.ts.map +1 -1
- package/dist/toolkit/audit/writer.js +45 -5
- package/dist/toolkit/audit/writer.js.map +1 -1
- package/dist/toolkit/connector_error.d.ts +12 -0
- package/dist/toolkit/connector_error.d.ts.map +1 -0
- package/dist/toolkit/connector_error.js +18 -0
- package/dist/toolkit/connector_error.js.map +1 -0
- package/dist/toolkit/guardrail.d.ts +12 -2
- package/dist/toolkit/guardrail.d.ts.map +1 -1
- package/dist/toolkit/guardrail.js +17 -3
- package/dist/toolkit/guardrail.js.map +1 -1
- package/dist/toolkit/http_client.d.ts +134 -0
- package/dist/toolkit/http_client.d.ts.map +1 -0
- package/dist/toolkit/http_client.js +385 -0
- package/dist/toolkit/http_client.js.map +1 -0
- package/dist/toolkit/index.d.ts +3 -0
- package/dist/toolkit/index.d.ts.map +1 -1
- package/dist/toolkit/index.js +5 -0
- package/dist/toolkit/index.js.map +1 -1
- package/dist/toolkit/usage/aggregate.d.ts.map +1 -1
- package/dist/toolkit/usage/aggregate.js +19 -3
- package/dist/toolkit/usage/aggregate.js.map +1 -1
- package/package.json +14 -2
- package/plugin-hooks/dispatcher.mjs +584 -0
- package/plugin-hooks/plugin-config.mjs +36 -0
- package/plugins/{aws-agent → aws-connector}/.claude-plugin/plugin.json +1 -1
- package/plugins/{aws-agent → aws-connector}/README.md +7 -7
- package/plugins/{aws-agent/bin/aws-agent → aws-connector/bin/aws-connector} +3 -3
- package/plugins/aws-connector/commands/aws-connector.md +6 -0
- package/plugins/{gcp-agent → aws-connector}/hooks/hooks.json +12 -11
- package/plugins/aws-connector/package.json +9 -0
- package/plugins/aws-connector/plugin-config.json +4 -0
- package/plugins/{aws-agent/skills/aws-agent → aws-connector/skills/aws-connector}/SKILL.md +5 -5
- package/plugins/confluence-connector/.claude-plugin/plugin.json +6 -0
- package/plugins/{confluence-agent → confluence-connector}/README.md +2 -2
- package/plugins/confluence-connector/bin/confluence-connector +17 -0
- package/plugins/confluence-connector/commands/confluence-connector.md +6 -0
- package/plugins/{jira-agent → confluence-connector}/hooks/hooks.json +12 -11
- package/plugins/confluence-connector/package.json +8 -0
- package/plugins/confluence-connector/plugin-config.json +4 -0
- package/plugins/confluence-connector/skills/confluence-connector/SKILL.md +146 -0
- package/plugins/{create-connector → connector-creator}/.claude-plugin/plugin.json +1 -1
- package/plugins/{create-connector → connector-creator}/README.md +2 -2
- package/plugins/connector-creator/skills/connector-creator/SKILL.md +412 -0
- package/plugins/connector-creator/skills/connector-creator/assets/templates/_runtime/connector-gate.mjs.tmpl +120 -0
- package/plugins/connector-creator/skills/connector-creator/assets/templates/composite/SKILL.md.tmpl +26 -0
- package/plugins/connector-creator/skills/connector-creator/assets/templates/composite/bin.tmpl +2 -0
- package/plugins/connector-creator/skills/connector-creator/assets/templates/composite/index.mjs.tmpl +35 -0
- package/plugins/connector-creator/skills/connector-creator/assets/templates/knowledge/SKILL.md.tmpl +23 -0
- package/plugins/connector-creator/skills/connector-creator/assets/templates/shell-gate/SKILL.md.tmpl +27 -0
- package/plugins/connector-creator/skills/connector-creator/assets/templates/shell-gate/gates.json.tmpl +5 -0
- package/plugins/connector-creator/skills/connector-creator/lib/connector-registry.mjs +43 -0
- package/plugins/connector-creator/skills/connector-creator/lib/settings-wiring.mjs +71 -0
- package/plugins/connector-creator/skills/connector-creator/references/connector-contract.md +79 -0
- package/plugins/connector-creator/skills/connector-creator/references/flavor-authoring.md +58 -0
- package/plugins/connector-creator/skills/connector-creator/references/research-patterns.md +51 -0
- package/plugins/{db-agent → db-connector}/.claude-plugin/plugin.json +3 -3
- package/plugins/{db-agent → db-connector}/README.md +2 -2
- package/plugins/{github-agent/bin/github-agent → db-connector/bin/db-connector} +3 -3
- package/plugins/db-connector/commands/db-connector.md +6 -0
- package/plugins/db-connector/gates.json +37 -0
- package/plugins/{db-agent → db-connector}/hooks/guardrails.json +4 -2
- package/plugins/{aws-agent → db-connector}/hooks/hooks.json +15 -11
- package/plugins/{db-agent → db-connector}/package.json +1 -1
- package/plugins/db-connector/plugin-config.json +5 -0
- package/plugins/{db-agent/skills/db-agent → db-connector/skills/db-connector}/SKILL.md +5 -5
- package/plugins/{gcp-agent → gcp-connector}/.claude-plugin/plugin.json +1 -1
- package/plugins/{gcp-agent → gcp-connector}/README.md +5 -5
- package/plugins/{gcp-agent/bin/gcp-agent → gcp-connector/bin/gcp-connector} +3 -3
- package/plugins/gcp-connector/commands/gcp-connector.md +6 -0
- package/plugins/{github-agent → gcp-connector}/hooks/hooks.json +12 -11
- package/plugins/gcp-connector/package.json +9 -0
- package/plugins/gcp-connector/plugin-config.json +4 -0
- package/plugins/{gcp-agent/skills/gcp-agent → gcp-connector/skills/gcp-connector}/SKILL.md +5 -5
- package/plugins/git-connector/.claude-plugin/plugin.json +6 -0
- package/plugins/git-connector/CONTRIBUTING.md +117 -0
- package/plugins/git-connector/README.md +94 -0
- package/plugins/git-connector/SECURITY.md +143 -0
- package/plugins/git-connector/gates.json +54 -0
- package/plugins/git-connector/hooks/hooks.json +25 -0
- package/plugins/git-connector/package.json +9 -0
- package/plugins/git-connector/plugin-config.json +4 -0
- package/plugins/{github-agent → github-connector}/.claude-plugin/plugin.json +1 -1
- package/plugins/github-connector/README.md +48 -0
- package/plugins/{confluence-agent/bin/confluence-agent → github-connector/bin/github-connector} +3 -3
- package/plugins/github-connector/commands/github-connector.md +6 -0
- package/plugins/github-connector/hooks/hooks.json +50 -0
- package/plugins/{jira-agent → github-connector}/package.json +1 -1
- package/plugins/github-connector/plugin-config.json +4 -0
- package/plugins/github-connector/skills/github-connector/SKILL.md +106 -0
- package/plugins/gitlab-connector/.claude-plugin/plugin.json +6 -0
- package/plugins/gitlab-connector/README.md +62 -0
- package/plugins/{db-agent/bin/db-agent → gitlab-connector/bin/gitlab-connector} +3 -3
- package/plugins/gitlab-connector/commands/gitlab-connector.md +6 -0
- package/plugins/gitlab-connector/hooks/hooks.json +50 -0
- package/plugins/{confluence-agent → gitlab-connector}/package.json +1 -1
- package/plugins/gitlab-connector/plugin-config.json +4 -0
- package/plugins/gitlab-connector/skills/gitlab-connector/SKILL.md +115 -0
- package/plugins/jira-connector/.claude-plugin/plugin.json +6 -0
- package/plugins/{jira-agent → jira-connector}/README.md +1 -1
- package/plugins/{jira-agent/bin/jira-agent → jira-connector/bin/jira-connector} +2 -2
- package/plugins/jira-connector/commands/jira-connector.md +6 -0
- package/plugins/jira-connector/hooks/hooks.json +50 -0
- package/plugins/{github-agent → jira-connector}/package.json +1 -1
- package/plugins/jira-connector/plugin-config.json +4 -0
- package/plugins/jira-connector/skills/jira-connector/SKILL.md +146 -0
- package/plugins/linear-connector/.claude-plugin/plugin.json +6 -0
- package/plugins/linear-connector/README.md +29 -0
- package/plugins/linear-connector/bin/linear-connector +17 -0
- package/plugins/linear-connector/commands/linear-connector.md +6 -0
- package/plugins/linear-connector/hooks/hooks.json +50 -0
- package/plugins/linear-connector/package.json +8 -0
- package/plugins/linear-connector/plugin-config.json +4 -0
- package/plugins/linear-connector/skills/linear-connector/SKILL.md +159 -0
- package/plugins/notion-connector/.claude-plugin/plugin.json +6 -0
- package/plugins/{notion-agent → notion-connector}/README.md +5 -5
- package/plugins/{notion-agent/bin/notion-agent → notion-connector/bin/notion-connector} +2 -2
- package/plugins/notion-connector/commands/notion-connector.md +6 -0
- package/plugins/notion-connector/hooks/hooks.json +50 -0
- package/plugins/notion-connector/package.json +8 -0
- package/plugins/notion-connector/plugin-config.json +4 -0
- package/plugins/notion-connector/skills/notion-connector/SKILL.md +141 -0
- package/dist/connectors/confluence/lib/confluence_error.d.ts +0 -13
- package/dist/connectors/confluence/lib/confluence_error.d.ts.map +0 -1
- package/dist/connectors/confluence/lib/confluence_error.js +0 -19
- package/dist/connectors/confluence/lib/confluence_error.js.map +0 -1
- package/dist/connectors/github/lib/github_error.d.ts +0 -11
- package/dist/connectors/github/lib/github_error.d.ts.map +0 -1
- package/dist/connectors/github/lib/github_error.js +0 -17
- package/dist/connectors/github/lib/github_error.js.map +0 -1
- package/dist/connectors/jira/lib/jira_error.d.ts +0 -11
- package/dist/connectors/jira/lib/jira_error.d.ts.map +0 -1
- package/dist/connectors/jira/lib/jira_error.js +0 -17
- package/dist/connectors/jira/lib/jira_error.js.map +0 -1
- package/dist/connectors/notion/lib/notion_error.d.ts +0 -12
- package/dist/connectors/notion/lib/notion_error.d.ts.map +0 -1
- package/dist/connectors/notion/lib/notion_error.js +0 -18
- package/dist/connectors/notion/lib/notion_error.js.map +0 -1
- package/plugins/aws-agent/commands/aws-agent.md +0 -6
- package/plugins/aws-agent/hooks/reminder.mjs +0 -16
- package/plugins/aws-agent/package.json +0 -9
- package/plugins/confluence-agent/.claude-plugin/plugin.json +0 -6
- package/plugins/confluence-agent/commands/confluence-agent.md +0 -6
- package/plugins/confluence-agent/hooks/hooks.json +0 -49
- package/plugins/confluence-agent/hooks/reminder.mjs +0 -25
- package/plugins/confluence-agent/skills/confluence-agent/SKILL.md +0 -40
- package/plugins/create-connector/skills/create-connector/SKILL.md +0 -252
- package/plugins/db-agent/commands/db-agent.md +0 -6
- package/plugins/db-agent/hooks/db-guard.mjs +0 -110
- package/plugins/db-agent/hooks/hooks.json +0 -61
- package/plugins/db-agent/hooks/reminder.mjs +0 -16
- package/plugins/gcp-agent/commands/gcp-agent.md +0 -6
- package/plugins/gcp-agent/hooks/reminder.mjs +0 -16
- package/plugins/gcp-agent/package.json +0 -9
- package/plugins/github-agent/README.md +0 -13
- package/plugins/github-agent/commands/github-agent.md +0 -6
- package/plugins/github-agent/hooks/reminder.mjs +0 -16
- package/plugins/github-agent/skills/github-agent/SKILL.md +0 -41
- package/plugins/jira-agent/.claude-plugin/plugin.json +0 -6
- package/plugins/jira-agent/commands/jira-agent.md +0 -6
- package/plugins/jira-agent/hooks/reminder.mjs +0 -16
- package/plugins/jira-agent/skills/jira-agent/SKILL.md +0 -37
- package/plugins/notion-agent/.claude-plugin/plugin.json +0 -6
- package/plugins/notion-agent/commands/notion-agent.md +0 -6
- package/plugins/notion-agent/hooks/hooks.json +0 -49
- package/plugins/notion-agent/hooks/reminder.mjs +0 -17
- package/plugins/notion-agent/package.json +0 -8
- package/plugins/notion-agent/skills/notion-agent/SKILL.md +0 -48
- /package/plugins/{create-connector/skills/create-connector → connector-creator/skills/connector-creator}/assets/templates/bin.tmpl +0 -0
- /package/plugins/{create-connector/skills/create-connector → connector-creator/skills/connector-creator}/assets/templates/connector-SKILL.md.tmpl +0 -0
- /package/plugins/{create-connector/skills/create-connector → connector-creator/skills/connector-creator}/assets/templates/index.mjs.tmpl +0 -0
- /package/plugins/{create-connector/skills/create-connector → connector-creator/skills/connector-creator}/assets/templates/tests-example.mjs.tmpl +0 -0
- /package/plugins/{create-connector/skills/create-connector → connector-creator/skills/connector-creator}/references/action-design.md +0 -0
- /package/plugins/{create-connector/skills/create-connector → connector-creator/skills/connector-creator}/references/auth-patterns.md +0 -0
- /package/plugins/{create-connector/skills/create-connector → connector-creator/skills/connector-creator}/references/connector-anatomy.md +0 -0
- /package/plugins/{create-connector/skills/create-connector/references/db-agent-pointer.md → connector-creator/skills/connector-creator/references/db-connector-pointer.md} +0 -0
- /package/plugins/{create-connector/skills/create-connector → connector-creator/skills/connector-creator}/references/plugin-layer.md +0 -0
- /package/plugins/{create-connector/skills/create-connector → connector-creator/skills/connector-creator}/references/template-sync.md +0 -0
- /package/plugins/{create-connector/skills/create-connector → connector-creator/skills/connector-creator}/references/verification.md +0 -0
|
@@ -19,6 +19,9 @@ export function aggregateRecords(sessionId, connector, records) {
|
|
|
19
19
|
let errors = 0;
|
|
20
20
|
let start = records[0].ts;
|
|
21
21
|
let end = records[0].ts;
|
|
22
|
+
let top1 = null;
|
|
23
|
+
let top2 = null;
|
|
24
|
+
let top3 = null;
|
|
22
25
|
for (const rec of records) {
|
|
23
26
|
totalBytes += rec.response_bytes;
|
|
24
27
|
totalTokens += rec.estimated_tokens;
|
|
@@ -28,6 +31,20 @@ export function aggregateRecords(sessionId, connector, records) {
|
|
|
28
31
|
start = rec.ts;
|
|
29
32
|
if (rec.ts > end)
|
|
30
33
|
end = rec.ts;
|
|
34
|
+
// ⚡ Bolt: Replace O(N log N) sort with O(N) Top-K manual tracking
|
|
35
|
+
// Avoids sorting the entire records array to find the 3 largest response_bytes.
|
|
36
|
+
if (!top1 || rec.response_bytes > top1.response_bytes) {
|
|
37
|
+
top3 = top2;
|
|
38
|
+
top2 = top1;
|
|
39
|
+
top1 = rec;
|
|
40
|
+
}
|
|
41
|
+
else if (!top2 || rec.response_bytes > top2.response_bytes) {
|
|
42
|
+
top3 = top2;
|
|
43
|
+
top2 = rec;
|
|
44
|
+
}
|
|
45
|
+
else if (!top3 || rec.response_bytes > top3.response_bytes) {
|
|
46
|
+
top3 = rec;
|
|
47
|
+
}
|
|
31
48
|
const slot = byAction[rec.action] ??
|
|
32
49
|
(byAction[rec.action] = {
|
|
33
50
|
calls: 0,
|
|
@@ -53,9 +70,8 @@ export function aggregateRecords(sessionId, connector, records) {
|
|
|
53
70
|
avg_ms: s.ms_count > 0 ? Math.round(s.ms_total / s.ms_count) : 0,
|
|
54
71
|
};
|
|
55
72
|
}
|
|
56
|
-
const top_responses = [
|
|
57
|
-
.
|
|
58
|
-
.slice(0, 3)
|
|
73
|
+
const top_responses = [top1, top2, top3]
|
|
74
|
+
.filter((r) => r !== null)
|
|
59
75
|
.map((r) => ({ action: r.action, response_bytes: r.response_bytes }));
|
|
60
76
|
return {
|
|
61
77
|
session_id: sessionId,
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"aggregate.js","sourceRoot":"","sources":["../../../src/toolkit/usage/aggregate.ts"],"names":[],"mappings":"AAOA,MAAM,UAAU,gBAAgB,CAC9B,SAAiB,EACjB,SAAiB,EACjB,OAAsB;IAEtB,IAAI,OAAO,CAAC,MAAM,KAAK,CAAC,EAAE,CAAC;QACzB,OAAO;YACL,UAAU,EAAE,SAAS;YACrB,SAAS;YACT,KAAK,EAAE,EAAE;YACT,GAAG,EAAE,EAAE;YACP,WAAW,EAAE,CAAC;YACd,oBAAoB,EAAE,CAAC;YACvB,sBAAsB,EAAE,CAAC;YACzB,UAAU,EAAE,CAAC;YACb,SAAS,EAAE,EAAE;YACb,aAAa,EAAE,EAAE;SAClB,CAAC;IACJ,CAAC;IAED,MAAM,QAAQ,
|
|
1
|
+
{"version":3,"file":"aggregate.js","sourceRoot":"","sources":["../../../src/toolkit/usage/aggregate.ts"],"names":[],"mappings":"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"}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "narai-primitives",
|
|
3
|
-
"version": "2.
|
|
3
|
+
"version": "2.2.0",
|
|
4
4
|
"description": "Read-only connectors + planning hub + connector framework + credential resolution, in one package. Bundles what was @narai/connector-toolkit, @narai/connector-config, @narai/connector-hub, the seven @narai/<svc>-agent-connector packages, and @narai/credential-providers.",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"main": "./dist/hub/index.js",
|
|
@@ -65,6 +65,14 @@
|
|
|
65
65
|
"./notion": {
|
|
66
66
|
"types": "./dist/connectors/notion/index.d.ts",
|
|
67
67
|
"import": "./dist/connectors/notion/index.js"
|
|
68
|
+
},
|
|
69
|
+
"./linear": {
|
|
70
|
+
"types": "./dist/connectors/linear/index.d.ts",
|
|
71
|
+
"import": "./dist/connectors/linear/index.js"
|
|
72
|
+
},
|
|
73
|
+
"./gitlab": {
|
|
74
|
+
"types": "./dist/connectors/gitlab/index.d.ts",
|
|
75
|
+
"import": "./dist/connectors/gitlab/index.js"
|
|
68
76
|
}
|
|
69
77
|
},
|
|
70
78
|
"bin": {
|
|
@@ -76,10 +84,12 @@
|
|
|
76
84
|
"github-agent-connector": "./dist/connectors/github/cli.js",
|
|
77
85
|
"jira-agent-connector": "./dist/connectors/jira/cli.js",
|
|
78
86
|
"notion-agent-connector": "./dist/connectors/notion/cli.js",
|
|
87
|
+
"linear-agent-connector": "./dist/connectors/linear/cli.js",
|
|
88
|
+
"gitlab-agent-connector": "./dist/connectors/gitlab/cli.js",
|
|
79
89
|
"usage-report": "./dist/toolkit/cli/usage-report.js"
|
|
80
90
|
},
|
|
81
91
|
"engines": {
|
|
82
|
-
"node": ">=20.
|
|
92
|
+
"node": ">=20.10.0"
|
|
83
93
|
},
|
|
84
94
|
"files": [
|
|
85
95
|
"dist/",
|
|
@@ -108,8 +118,10 @@
|
|
|
108
118
|
},
|
|
109
119
|
"dependencies": {
|
|
110
120
|
"@anthropic-ai/claude-agent-sdk": "^0.2.119",
|
|
121
|
+
"adf-to-markdown": "^1.0.1",
|
|
111
122
|
"better-sqlite3": "^12.9.0",
|
|
112
123
|
"js-yaml": "^4.1.1",
|
|
124
|
+
"marklassian": "^1.2.1",
|
|
113
125
|
"zod": "^3.23.0"
|
|
114
126
|
},
|
|
115
127
|
"optionalDependencies": {
|
|
@@ -0,0 +1,584 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
/**
|
|
3
|
+
* Shared PreToolUse / PostToolUse / SessionStart / SessionEnd dispatcher
|
|
4
|
+
* for narai-primitives builtin Claude Code plugins.
|
|
5
|
+
*
|
|
6
|
+
* Usage:
|
|
7
|
+
* node dispatcher.mjs <event>
|
|
8
|
+
*
|
|
9
|
+
* Where <event> is one of: session-start, pre-tool-use, post-tool-use,
|
|
10
|
+
* session-end. Reads ${CLAUDE_PLUGIN_ROOT}/plugin-config.json for the
|
|
11
|
+
* plugin's identity and routes to the appropriate handler.
|
|
12
|
+
*
|
|
13
|
+
* Best-effort: handler-internal failures are logged to stderr but do not
|
|
14
|
+
* block tool execution. Argument / configuration errors exit with a
|
|
15
|
+
* non-zero code so Claude Code surfaces them to the operator.
|
|
16
|
+
*/
|
|
17
|
+
import * as fs from "node:fs";
|
|
18
|
+
import * as path from "node:path";
|
|
19
|
+
import { fileURLToPath, pathToFileURL } from "node:url";
|
|
20
|
+
import { parsePluginConfig } from "./plugin-config.mjs";
|
|
21
|
+
|
|
22
|
+
const VALID_EVENTS = new Set([
|
|
23
|
+
"session-start",
|
|
24
|
+
"pre-tool-use",
|
|
25
|
+
"post-tool-use",
|
|
26
|
+
"session-end",
|
|
27
|
+
]);
|
|
28
|
+
|
|
29
|
+
// Only run main() when invoked as a CLI, not when imported by tests.
|
|
30
|
+
// Resolve symlinks on both sides so the smart-bootstrap dedup path
|
|
31
|
+
// (where `node_modules/narai-primitives` is symlinked from a sibling
|
|
32
|
+
// plugin) still recognizes itself as the CLI entrypoint.
|
|
33
|
+
const isMainScript = (() => {
|
|
34
|
+
if (process.argv[1] === undefined) return false;
|
|
35
|
+
try {
|
|
36
|
+
const realArgv = fs.realpathSync(process.argv[1]);
|
|
37
|
+
const realSelf = fs.realpathSync(fileURLToPath(import.meta.url));
|
|
38
|
+
return realArgv === realSelf;
|
|
39
|
+
} catch {
|
|
40
|
+
// Fall back to literal comparison if realpath can't resolve.
|
|
41
|
+
return process.argv[1] === fileURLToPath(import.meta.url);
|
|
42
|
+
}
|
|
43
|
+
})();
|
|
44
|
+
|
|
45
|
+
if (isMainScript) {
|
|
46
|
+
main().catch((err) => {
|
|
47
|
+
process.stderr.write(`dispatcher: ${err?.message ?? err}\n`);
|
|
48
|
+
process.exit(1);
|
|
49
|
+
});
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
async function main() {
|
|
53
|
+
const event = process.argv[2];
|
|
54
|
+
if (!VALID_EVENTS.has(event)) {
|
|
55
|
+
process.stderr.write(
|
|
56
|
+
`dispatcher: unknown event '${event}' (expected one of ${[...VALID_EVENTS].join(", ")})\n`,
|
|
57
|
+
);
|
|
58
|
+
process.exit(2);
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
const root = process.env.CLAUDE_PLUGIN_ROOT;
|
|
62
|
+
if (!root) {
|
|
63
|
+
process.stderr.write("dispatcher: CLAUDE_PLUGIN_ROOT not set\n");
|
|
64
|
+
process.exit(2);
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
const cfgPath = path.join(root, "plugin-config.json");
|
|
68
|
+
if (!fs.existsSync(cfgPath)) {
|
|
69
|
+
process.stderr.write(`dispatcher: missing ${cfgPath}\n`);
|
|
70
|
+
process.exit(2);
|
|
71
|
+
}
|
|
72
|
+
const cfg = parsePluginConfig(fs.readFileSync(cfgPath, "utf-8"));
|
|
73
|
+
|
|
74
|
+
switch (event) {
|
|
75
|
+
case "session-start":
|
|
76
|
+
await onSessionStart(cfg);
|
|
77
|
+
break;
|
|
78
|
+
case "pre-tool-use":
|
|
79
|
+
await onPreToolUse(cfg);
|
|
80
|
+
break;
|
|
81
|
+
case "post-tool-use":
|
|
82
|
+
await onPostToolUse(cfg);
|
|
83
|
+
break;
|
|
84
|
+
case "session-end":
|
|
85
|
+
await onSessionEnd(cfg);
|
|
86
|
+
break;
|
|
87
|
+
}
|
|
88
|
+
process.exit(0);
|
|
89
|
+
}
|
|
90
|
+
|
|
91
|
+
async function loadToolkit() {
|
|
92
|
+
const { existsSync } = fs;
|
|
93
|
+
const { homedir } = await import("node:os");
|
|
94
|
+
const { fileURLToPath, pathToFileURL } = await import("node:url");
|
|
95
|
+
const __dirname = path.dirname(fileURLToPath(import.meta.url));
|
|
96
|
+
const candidates = [
|
|
97
|
+
// 1. Bundled: dispatcher at narai-primitives/plugin-hooks/; toolkit at narai-primitives/dist/toolkit/guardrail.js
|
|
98
|
+
path.join(__dirname, "..", "dist", "toolkit", "guardrail.js"),
|
|
99
|
+
// 2. Claude Code plugin install
|
|
100
|
+
process.env.CLAUDE_PLUGIN_DATA
|
|
101
|
+
? path.join(process.env.CLAUDE_PLUGIN_DATA, "node_modules", "narai-primitives", "dist", "toolkit", "guardrail.js")
|
|
102
|
+
: null,
|
|
103
|
+
].filter((p) => p !== null);
|
|
104
|
+
for (const p of candidates) {
|
|
105
|
+
if (!existsSync(p)) continue;
|
|
106
|
+
try {
|
|
107
|
+
return await import(pathToFileURL(p).href);
|
|
108
|
+
} catch {
|
|
109
|
+
// try next
|
|
110
|
+
}
|
|
111
|
+
}
|
|
112
|
+
return null;
|
|
113
|
+
}
|
|
114
|
+
|
|
115
|
+
/**
|
|
116
|
+
* Lazily load the db audit module from dist. Returns the module namespace
|
|
117
|
+
* ({ enableAudit, logEvent, scrubSqlSecrets, ... }) or null if unavailable.
|
|
118
|
+
* Only used when NARAI_AUDIT_PATH is set, so the common path stays cheap.
|
|
119
|
+
*/
|
|
120
|
+
async function loadAudit() {
|
|
121
|
+
const { existsSync } = fs;
|
|
122
|
+
const __dirname = path.dirname(fileURLToPath(import.meta.url));
|
|
123
|
+
const candidates = [
|
|
124
|
+
path.join(__dirname, "..", "dist", "connectors", "db", "lib", "audit.js"),
|
|
125
|
+
process.env.CLAUDE_PLUGIN_DATA
|
|
126
|
+
? path.join(process.env.CLAUDE_PLUGIN_DATA, "node_modules", "narai-primitives", "dist", "connectors", "db", "lib", "audit.js")
|
|
127
|
+
: null,
|
|
128
|
+
].filter((p) => p !== null);
|
|
129
|
+
for (const p of candidates) {
|
|
130
|
+
if (!existsSync(p)) continue;
|
|
131
|
+
try {
|
|
132
|
+
return await import(pathToFileURL(p).href);
|
|
133
|
+
} catch {
|
|
134
|
+
// try next
|
|
135
|
+
}
|
|
136
|
+
}
|
|
137
|
+
return null;
|
|
138
|
+
}
|
|
139
|
+
|
|
140
|
+
/**
|
|
141
|
+
* Walk siblings of `pluginDataDir` looking for a `node_modules/narai-primitives`
|
|
142
|
+
* at `wantedVersion`. Returns the matched node_modules path, or null if no
|
|
143
|
+
* usable sibling found. Used to skip redundant npm install when N builtin
|
|
144
|
+
* plugins are loaded in the same Claude Code session.
|
|
145
|
+
*/
|
|
146
|
+
export function findSiblingInstall(pluginDataDir, wantedVersion) {
|
|
147
|
+
const parent = path.dirname(pluginDataDir);
|
|
148
|
+
let entries;
|
|
149
|
+
try {
|
|
150
|
+
entries = fs.readdirSync(parent, { withFileTypes: true });
|
|
151
|
+
} catch {
|
|
152
|
+
return null;
|
|
153
|
+
}
|
|
154
|
+
for (const entry of entries) {
|
|
155
|
+
if (!entry.isDirectory()) continue;
|
|
156
|
+
const candidate = path.join(parent, entry.name);
|
|
157
|
+
if (candidate === pluginDataDir) continue;
|
|
158
|
+
const pkgJson = path.join(
|
|
159
|
+
candidate,
|
|
160
|
+
"node_modules",
|
|
161
|
+
"narai-primitives",
|
|
162
|
+
"package.json",
|
|
163
|
+
);
|
|
164
|
+
if (!fs.existsSync(pkgJson)) continue;
|
|
165
|
+
try {
|
|
166
|
+
const meta = JSON.parse(fs.readFileSync(pkgJson, "utf-8"));
|
|
167
|
+
if (meta.version === wantedVersion) {
|
|
168
|
+
return path.join(candidate, "node_modules");
|
|
169
|
+
}
|
|
170
|
+
} catch {
|
|
171
|
+
continue;
|
|
172
|
+
}
|
|
173
|
+
}
|
|
174
|
+
return null;
|
|
175
|
+
}
|
|
176
|
+
|
|
177
|
+
async function onSessionStart(cfg) {
|
|
178
|
+
const pluginRoot = process.env.CLAUDE_PLUGIN_ROOT;
|
|
179
|
+
const pluginData = process.env.CLAUDE_PLUGIN_DATA;
|
|
180
|
+
if (!pluginRoot || !pluginData) return;
|
|
181
|
+
|
|
182
|
+
await ensureBootstrap(pluginRoot, pluginData);
|
|
183
|
+
|
|
184
|
+
// Best-effort: emit nudge banner if the toolkit is reachable.
|
|
185
|
+
try {
|
|
186
|
+
const reminderPath = path.join(
|
|
187
|
+
pluginData,
|
|
188
|
+
"node_modules",
|
|
189
|
+
"narai-primitives",
|
|
190
|
+
"dist",
|
|
191
|
+
"toolkit",
|
|
192
|
+
"plugin",
|
|
193
|
+
"reminder.js",
|
|
194
|
+
);
|
|
195
|
+
if (fs.existsSync(reminderPath)) {
|
|
196
|
+
// pathToFileURL is required on Windows — Node refuses raw absolute
|
|
197
|
+
// paths as ESM specifiers (ERR_UNSUPPORTED_ESM_URL_SCHEME).
|
|
198
|
+
const mod = await import(pathToFileURL(reminderPath).href);
|
|
199
|
+
const decision = mod.evaluateNudge({ connectors: [cfg.name] });
|
|
200
|
+
if (decision.nudge) process.stdout.write(decision.banner + "\n");
|
|
201
|
+
}
|
|
202
|
+
} catch (err) {
|
|
203
|
+
process.stderr.write(`dispatcher: nudge failed (${err.message})\n`);
|
|
204
|
+
}
|
|
205
|
+
|
|
206
|
+
// Best-effort: stale-summarize.
|
|
207
|
+
try {
|
|
208
|
+
const stalePath = path.join(
|
|
209
|
+
pluginData,
|
|
210
|
+
"node_modules",
|
|
211
|
+
"narai-primitives",
|
|
212
|
+
"plugin-hooks",
|
|
213
|
+
"stale-summarize.mjs",
|
|
214
|
+
);
|
|
215
|
+
if (fs.existsSync(stalePath)) {
|
|
216
|
+
process.env.USAGE_CONNECTOR_NAME = cfg.name;
|
|
217
|
+
await import(pathToFileURL(stalePath).href);
|
|
218
|
+
}
|
|
219
|
+
} catch (err) {
|
|
220
|
+
process.stderr.write(`dispatcher: stale-summarize failed (${err.message})\n`);
|
|
221
|
+
}
|
|
222
|
+
}
|
|
223
|
+
|
|
224
|
+
async function onPreToolUse(cfg) {
|
|
225
|
+
const stdin = await readStdin();
|
|
226
|
+
if (!stdin) return;
|
|
227
|
+
let payload;
|
|
228
|
+
try {
|
|
229
|
+
payload = JSON.parse(stdin);
|
|
230
|
+
} catch {
|
|
231
|
+
return;
|
|
232
|
+
}
|
|
233
|
+
// Derive the tool being gated and the text to scan. Bash scans the
|
|
234
|
+
// command; Write scans the file content; Edit scans the incoming text
|
|
235
|
+
// (new_string only — old_string is the text being removed). `command`
|
|
236
|
+
// therefore holds the candidate text for whichever tool fired.
|
|
237
|
+
let scanTool;
|
|
238
|
+
let command;
|
|
239
|
+
if (payload.tool_name === "Bash") {
|
|
240
|
+
scanTool = "Bash";
|
|
241
|
+
command = payload.tool_input?.command;
|
|
242
|
+
} else if (payload.tool_name === "Write") {
|
|
243
|
+
scanTool = "Write";
|
|
244
|
+
command = payload.tool_input?.content;
|
|
245
|
+
} else if (payload.tool_name === "Edit") {
|
|
246
|
+
scanTool = "Edit";
|
|
247
|
+
command = payload.tool_input?.new_string;
|
|
248
|
+
} else {
|
|
249
|
+
return;
|
|
250
|
+
}
|
|
251
|
+
if (typeof command !== "string" || command.length === 0) return;
|
|
252
|
+
|
|
253
|
+
const decisions = [];
|
|
254
|
+
|
|
255
|
+
// 1. db-guard (Bash only; only if kind=db, and not opted out via user_config).
|
|
256
|
+
// The token-blocklist engine is command-shaped and meaningless for file content.
|
|
257
|
+
if (scanTool === "Bash" && cfg.kind === "db" && process.env.DB_AGENT_GUARDRAILS !== "off") {
|
|
258
|
+
const guardrailsPath = path.join(
|
|
259
|
+
process.env.CLAUDE_PLUGIN_ROOT,
|
|
260
|
+
"hooks",
|
|
261
|
+
"guardrails.json",
|
|
262
|
+
);
|
|
263
|
+
if (fs.existsSync(guardrailsPath)) {
|
|
264
|
+
try {
|
|
265
|
+
const toolkit = await loadToolkit();
|
|
266
|
+
if (toolkit && typeof toolkit.findBlockingRule === "function") {
|
|
267
|
+
const { findBlockingRule, defaultDenyMessage, loadGuardrailManifest } = toolkit;
|
|
268
|
+
const manifest = loadGuardrailManifest(guardrailsPath);
|
|
269
|
+
const match = findBlockingRule(command, [manifest]);
|
|
270
|
+
if (match) {
|
|
271
|
+
decisions.push({
|
|
272
|
+
decision: "deny",
|
|
273
|
+
reason: defaultDenyMessage(match),
|
|
274
|
+
});
|
|
275
|
+
}
|
|
276
|
+
} else if (effectiveEnforcement(undefined) === "fail_closed") {
|
|
277
|
+
decisions.push({
|
|
278
|
+
decision: "deny",
|
|
279
|
+
reason: "fail-closed enforcement: db guardrail engine is unavailable",
|
|
280
|
+
});
|
|
281
|
+
}
|
|
282
|
+
} catch (err) {
|
|
283
|
+
process.stderr.write(`dispatcher: db-guard failed (${err.message})\n`);
|
|
284
|
+
if (effectiveEnforcement(undefined) === "fail_closed") {
|
|
285
|
+
decisions.push({
|
|
286
|
+
decision: "deny",
|
|
287
|
+
reason: `fail-closed enforcement: db guardrail manifest could not be evaluated (${err.message})`,
|
|
288
|
+
});
|
|
289
|
+
}
|
|
290
|
+
}
|
|
291
|
+
}
|
|
292
|
+
}
|
|
293
|
+
|
|
294
|
+
// 2. user-connector gates from $HOME and cwd. Mirror connector-gate.mjs:
|
|
295
|
+
// honor NARAI_GATE_DISABLE (comma-separated rule names) so operators
|
|
296
|
+
// can silence a noisy rule without editing gates.json.
|
|
297
|
+
const disabled = new Set(
|
|
298
|
+
(process.env.NARAI_GATE_DISABLE ?? "")
|
|
299
|
+
.split(",")
|
|
300
|
+
.map((s) => s.trim())
|
|
301
|
+
.filter(Boolean),
|
|
302
|
+
);
|
|
303
|
+
// 3. plugin-shipped gates.json at CLAUDE_PLUGIN_ROOT/gates.json — lets
|
|
304
|
+
// hook-only plugins (e.g. git-connector) ship default rules out-of-the-box.
|
|
305
|
+
const pluginGatesFile = path.join(process.env.CLAUDE_PLUGIN_ROOT, "gates.json");
|
|
306
|
+
if (fs.existsSync(pluginGatesFile)) {
|
|
307
|
+
try {
|
|
308
|
+
const gateCfg = JSON.parse(fs.readFileSync(pluginGatesFile, "utf-8"));
|
|
309
|
+
applyGatesManifest(gateCfg, cfg.name, command, disabled, decisions, scanTool);
|
|
310
|
+
} catch (err) {
|
|
311
|
+
process.stderr.write(
|
|
312
|
+
`dispatcher: plugin-root gate scan failed (${err.message})\n`,
|
|
313
|
+
);
|
|
314
|
+
if (effectiveEnforcement(undefined) === "fail_closed") {
|
|
315
|
+
decisions.push({
|
|
316
|
+
decision: "deny",
|
|
317
|
+
reason: `fail-closed enforcement: gates manifest at ${pluginGatesFile} could not be parsed`,
|
|
318
|
+
});
|
|
319
|
+
}
|
|
320
|
+
}
|
|
321
|
+
}
|
|
322
|
+
|
|
323
|
+
// 4. user-connector gates from $HOME and cwd.
|
|
324
|
+
const home = process.env.HOME ?? "";
|
|
325
|
+
const cwd = process.cwd();
|
|
326
|
+
for (const root of [home, cwd]) {
|
|
327
|
+
if (!root) continue;
|
|
328
|
+
const gatesDir = path.join(root, ".connectors", "connectors");
|
|
329
|
+
if (!fs.existsSync(gatesDir)) continue;
|
|
330
|
+
let slugs;
|
|
331
|
+
try {
|
|
332
|
+
slugs = fs
|
|
333
|
+
.readdirSync(gatesDir, { withFileTypes: true })
|
|
334
|
+
.filter((e) => e.isDirectory())
|
|
335
|
+
.map((e) => e.name);
|
|
336
|
+
} catch {
|
|
337
|
+
continue;
|
|
338
|
+
}
|
|
339
|
+
for (const slug of slugs) {
|
|
340
|
+
const gatesFile = path.join(gatesDir, slug, "gates.json");
|
|
341
|
+
if (!fs.existsSync(gatesFile)) continue;
|
|
342
|
+
try {
|
|
343
|
+
const gateCfg = JSON.parse(fs.readFileSync(gatesFile, "utf-8"));
|
|
344
|
+
applyGatesManifest(gateCfg, slug, command, disabled, decisions, scanTool);
|
|
345
|
+
} catch (err) {
|
|
346
|
+
process.stderr.write(
|
|
347
|
+
`dispatcher: gate scan failed for ${gatesFile} (${err.message})\n`,
|
|
348
|
+
);
|
|
349
|
+
if (effectiveEnforcement(undefined) === "fail_closed") {
|
|
350
|
+
decisions.push({
|
|
351
|
+
decision: "deny",
|
|
352
|
+
reason: `fail-closed enforcement: gates manifest at ${gatesFile} could not be parsed`,
|
|
353
|
+
});
|
|
354
|
+
}
|
|
355
|
+
}
|
|
356
|
+
}
|
|
357
|
+
}
|
|
358
|
+
|
|
359
|
+
if (decisions.length === 0) return;
|
|
360
|
+
const rank = { deny: 2, ask: 1, allow: 0 };
|
|
361
|
+
decisions.sort((a, b) => rank[b.decision] - rank[a.decision]);
|
|
362
|
+
const winner = decisions[0];
|
|
363
|
+
process.stdout.write(JSON.stringify({
|
|
364
|
+
hookSpecificOutput: {
|
|
365
|
+
hookEventName: "PreToolUse",
|
|
366
|
+
permissionDecision: winner.decision,
|
|
367
|
+
permissionDecisionReason: winner.reason,
|
|
368
|
+
},
|
|
369
|
+
}));
|
|
370
|
+
|
|
371
|
+
// Best-effort audit of blocked/escalated decisions. Only runs when an
|
|
372
|
+
// audit destination is configured, keeping the common allow path cheap.
|
|
373
|
+
const auditPath = process.env.NARAI_AUDIT_PATH;
|
|
374
|
+
if (auditPath && (winner.decision === "deny" || winner.decision === "ask")) {
|
|
375
|
+
try {
|
|
376
|
+
const audit = await loadAudit();
|
|
377
|
+
if (audit && typeof audit.logEvent === "function") {
|
|
378
|
+
audit.enableAudit(auditPath);
|
|
379
|
+
const scrub = typeof audit.scrubSqlSecrets === "function"
|
|
380
|
+
? audit.scrubSqlSecrets
|
|
381
|
+
: (s) => s;
|
|
382
|
+
audit.logEvent({
|
|
383
|
+
event_type: winner.decision === "deny" ? "guardrail_deny" : "guardrail_ask",
|
|
384
|
+
details: { tool: payload.tool_name, command: scrub(command), reason: winner.reason },
|
|
385
|
+
});
|
|
386
|
+
}
|
|
387
|
+
} catch (err) {
|
|
388
|
+
process.stderr.write(`dispatcher: decision audit failed (${err.message})\n`);
|
|
389
|
+
}
|
|
390
|
+
}
|
|
391
|
+
}
|
|
392
|
+
|
|
393
|
+
async function readStdin() {
|
|
394
|
+
const chunks = [];
|
|
395
|
+
for await (const chunk of process.stdin) chunks.push(chunk);
|
|
396
|
+
return Buffer.concat(chunks).toString("utf-8").trim();
|
|
397
|
+
}
|
|
398
|
+
async function onPostToolUse(cfg) {
|
|
399
|
+
const pluginData = process.env.CLAUDE_PLUGIN_DATA;
|
|
400
|
+
if (!pluginData) return;
|
|
401
|
+
const usagePath = path.join(
|
|
402
|
+
pluginData,
|
|
403
|
+
"node_modules",
|
|
404
|
+
"narai-primitives",
|
|
405
|
+
"plugin-hooks",
|
|
406
|
+
"usage-record.mjs",
|
|
407
|
+
);
|
|
408
|
+
if (!fs.existsSync(usagePath)) return;
|
|
409
|
+
process.env.USAGE_CONNECTOR_NAME = cfg.name;
|
|
410
|
+
if (cfg.binPath) process.env.USAGE_BIN_HINT = cfg.binPath;
|
|
411
|
+
try {
|
|
412
|
+
await import(pathToFileURL(usagePath).href);
|
|
413
|
+
} catch (err) {
|
|
414
|
+
process.stderr.write(`dispatcher: usage-record failed (${err.message})\n`);
|
|
415
|
+
}
|
|
416
|
+
}
|
|
417
|
+
async function onSessionEnd(cfg) {
|
|
418
|
+
const pluginData = process.env.CLAUDE_PLUGIN_DATA;
|
|
419
|
+
if (!pluginData) return;
|
|
420
|
+
const summaryPath = path.join(
|
|
421
|
+
pluginData,
|
|
422
|
+
"node_modules",
|
|
423
|
+
"narai-primitives",
|
|
424
|
+
"plugin-hooks",
|
|
425
|
+
"session-summary.mjs",
|
|
426
|
+
);
|
|
427
|
+
if (!fs.existsSync(summaryPath)) return;
|
|
428
|
+
// session-summary.mjs only runs its `main()` when it's the CLI entry
|
|
429
|
+
// point (`import.meta.url === file://${process.argv[1]}`). Importing it
|
|
430
|
+
// here would be a no-op; spawn it as a subprocess so the guard passes.
|
|
431
|
+
try {
|
|
432
|
+
const { spawnSync } = await import("node:child_process");
|
|
433
|
+
spawnSync("node", [summaryPath], {
|
|
434
|
+
stdio: "inherit",
|
|
435
|
+
env: { ...process.env, USAGE_CONNECTOR_NAME: cfg.name },
|
|
436
|
+
});
|
|
437
|
+
} catch (err) {
|
|
438
|
+
process.stderr.write(`dispatcher: session-summary failed (${err.message})\n`);
|
|
439
|
+
}
|
|
440
|
+
}
|
|
441
|
+
|
|
442
|
+
/**
|
|
443
|
+
* Ensure narai-primitives is installed in pluginData. Tries (in order):
|
|
444
|
+
* 1. Skip if already installed at the right version.
|
|
445
|
+
* 2. Symlink from a sibling plugin's node_modules.
|
|
446
|
+
* 3. npm install in pluginData.
|
|
447
|
+
*
|
|
448
|
+
* Best-effort: all branches return without throwing. The caller proceeds
|
|
449
|
+
* to side effects (nudge, stale-summarize, etc.) even when the install
|
|
450
|
+
* is cached, so recurring per-session behavior fires every run.
|
|
451
|
+
*/
|
|
452
|
+
async function ensureBootstrap(pluginRoot, pluginData) {
|
|
453
|
+
const rootPkg = path.join(pluginRoot, "package.json");
|
|
454
|
+
if (!fs.existsSync(rootPkg)) return;
|
|
455
|
+
let rootMeta;
|
|
456
|
+
try {
|
|
457
|
+
rootMeta = JSON.parse(fs.readFileSync(rootPkg, "utf-8"));
|
|
458
|
+
} catch {
|
|
459
|
+
return;
|
|
460
|
+
}
|
|
461
|
+
// Read the wanted narai-primitives version from the plugin's
|
|
462
|
+
// `dependencies` block (e.g. `"^2.1.3"`) and strip the semver
|
|
463
|
+
// prefix so the equality check matches the installed package's
|
|
464
|
+
// resolved version field (e.g. `"2.1.3"`). The plugin's own
|
|
465
|
+
// `version` is independent and not interchangeable.
|
|
466
|
+
const depRange = rootMeta.dependencies?.["narai-primitives"];
|
|
467
|
+
const wantVersion = depRange?.replace(/^[\^~>=< ]+/, "");
|
|
468
|
+
if (!wantVersion) return;
|
|
469
|
+
|
|
470
|
+
fs.mkdirSync(pluginData, { recursive: true });
|
|
471
|
+
|
|
472
|
+
const myInstall = path.join(pluginData, "node_modules", "narai-primitives");
|
|
473
|
+
if (fs.existsSync(myInstall)) {
|
|
474
|
+
try {
|
|
475
|
+
const installed = JSON.parse(
|
|
476
|
+
fs.readFileSync(path.join(myInstall, "package.json"), "utf-8"),
|
|
477
|
+
);
|
|
478
|
+
if (installed.version === wantVersion) return;
|
|
479
|
+
} catch {
|
|
480
|
+
// Fall through to re-install.
|
|
481
|
+
}
|
|
482
|
+
}
|
|
483
|
+
|
|
484
|
+
const sibling = findSiblingInstall(pluginData, wantVersion);
|
|
485
|
+
if (sibling !== null) {
|
|
486
|
+
try {
|
|
487
|
+
const myNodeModules = path.join(pluginData, "node_modules");
|
|
488
|
+
if (fs.existsSync(myNodeModules)) {
|
|
489
|
+
fs.rmSync(myNodeModules, { recursive: true, force: true });
|
|
490
|
+
}
|
|
491
|
+
fs.symlinkSync(sibling, myNodeModules, "dir");
|
|
492
|
+
return;
|
|
493
|
+
} catch {
|
|
494
|
+
// Fall through to install.
|
|
495
|
+
}
|
|
496
|
+
}
|
|
497
|
+
|
|
498
|
+
fs.copyFileSync(rootPkg, path.join(pluginData, "package.json"));
|
|
499
|
+
const { spawnSync } = await import("node:child_process");
|
|
500
|
+
spawnSync("npm", ["install", "--no-audit", "--no-fund"], {
|
|
501
|
+
cwd: pluginData,
|
|
502
|
+
stdio: "inherit",
|
|
503
|
+
});
|
|
504
|
+
}
|
|
505
|
+
|
|
506
|
+
/**
|
|
507
|
+
* Resolve the effective enforcement posture from the global
|
|
508
|
+
* NARAI_GATE_ENFORCEMENT env var and an optional manifest-level field.
|
|
509
|
+
* Strictest wins: fail-closed if either requests it, else fail-open.
|
|
510
|
+
*/
|
|
511
|
+
export function effectiveEnforcement(manifestEnforcement) {
|
|
512
|
+
const env = process.env.NARAI_GATE_ENFORCEMENT;
|
|
513
|
+
if (env === "fail_closed" || manifestEnforcement === "fail_closed") {
|
|
514
|
+
return "fail_closed";
|
|
515
|
+
}
|
|
516
|
+
return "fail_open";
|
|
517
|
+
}
|
|
518
|
+
|
|
519
|
+
/**
|
|
520
|
+
* Apply a parsed gates.json manifest to a command. Rules with invalid
|
|
521
|
+
* shape, disabled names, or uncompilable patterns are skipped. Anchored
|
|
522
|
+
* patterns match per-segment so chaining (`echo ok; psql ...`) can't bypass.
|
|
523
|
+
*
|
|
524
|
+
* Under fail-closed enforcement (env var or the manifest's `enforcement`
|
|
525
|
+
* field), a rule whose pattern will not compile becomes a hard deny instead
|
|
526
|
+
* of being silently skipped — we cannot prove the command is safe.
|
|
527
|
+
*/
|
|
528
|
+
export function applyGatesManifest(manifest, source, text, disabled, decisions, scanTool = "Bash") {
|
|
529
|
+
const enforcement = effectiveEnforcement(manifest.enforcement);
|
|
530
|
+
// Bash commands are split on chaining operators so anchored rules apply
|
|
531
|
+
// per-segment. File content (Write/Edit) is matched as a single unit so
|
|
532
|
+
// characters like `;` or `|` inside the file do not fragment it.
|
|
533
|
+
const segments = scanTool === "Bash" ? splitCompound(text) : [text];
|
|
534
|
+
for (const rule of manifest.rules ?? []) {
|
|
535
|
+
if (
|
|
536
|
+
!["deny", "ask", "allow"].includes(rule.decision) ||
|
|
537
|
+
typeof rule.pattern !== "string"
|
|
538
|
+
) continue;
|
|
539
|
+
if (typeof rule.name === "string" && disabled.has(rule.name)) continue;
|
|
540
|
+
// A rule applies to a tool only if listed in `applies_to`. Default is
|
|
541
|
+
// Bash-only, so every existing rule keeps its current behavior and is
|
|
542
|
+
// skipped on Write/Edit unless it explicitly opts in.
|
|
543
|
+
const appliesTo = Array.isArray(rule.applies_to) ? rule.applies_to : ["Bash"];
|
|
544
|
+
if (!appliesTo.includes(scanTool)) continue;
|
|
545
|
+
let re;
|
|
546
|
+
try { re = new RegExp(rule.pattern); } catch {
|
|
547
|
+
if (enforcement === "fail_closed") {
|
|
548
|
+
decisions.push({
|
|
549
|
+
decision: "deny",
|
|
550
|
+
reason: `fail-closed enforcement: ${source} gate rule '${rule.name ?? "rule"}' has an invalid pattern`,
|
|
551
|
+
});
|
|
552
|
+
}
|
|
553
|
+
continue;
|
|
554
|
+
}
|
|
555
|
+
for (const segment of segments) {
|
|
556
|
+
if (re.test(segment)) {
|
|
557
|
+
decisions.push({
|
|
558
|
+
decision: rule.decision,
|
|
559
|
+
reason: rule.reason ?? `${source} gate: ${rule.name ?? "rule"}`,
|
|
560
|
+
});
|
|
561
|
+
break;
|
|
562
|
+
}
|
|
563
|
+
}
|
|
564
|
+
}
|
|
565
|
+
}
|
|
566
|
+
|
|
567
|
+
/**
|
|
568
|
+
* Split a bash command on chaining operators so anchored gate rules
|
|
569
|
+
* apply per-segment. Mirrors connector-gate.mjs's behavior.
|
|
570
|
+
*/
|
|
571
|
+
function splitCompound(cmd) {
|
|
572
|
+
const parts = cmd.split(/\s*(?:&&|\|\||;|\|)\s*/);
|
|
573
|
+
return parts
|
|
574
|
+
.map((p) => stripPrefix(p.trim()))
|
|
575
|
+
.filter((p) => p.length > 0);
|
|
576
|
+
}
|
|
577
|
+
|
|
578
|
+
function stripPrefix(s) {
|
|
579
|
+
let cur = s;
|
|
580
|
+
while (/^[A-Za-z_][A-Za-z0-9_]*=\S*\s+/.test(cur)) {
|
|
581
|
+
cur = cur.replace(/^[A-Za-z_][A-Za-z0-9_]*=\S*\s+/, "");
|
|
582
|
+
}
|
|
583
|
+
return cur.replace(/^(sudo|nice|time)\s+/, "");
|
|
584
|
+
}
|