kira-glasses-bridge 2.19.0 → 2.20.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/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "kira-glasses-bridge",
3
- "version": "2.19.0",
3
+ "version": "2.20.0",
4
4
  "description": "Connects your Mac to Kira, the assistant for Even G2 glasses: Claude Code sessions, files, folders and apps, by voice.",
5
5
  "type": "module",
6
6
  "bin": {
@@ -31,11 +31,60 @@ export const WEB = new Set(['WebFetch', 'WebSearch'])
31
31
  /** Claude Code's name for the tools of Kira's MCP server (mcp__<server>__<tool>). */
32
32
  export const KIRA_TOOLS = 'mcp__kira__'
33
33
 
34
- /** Read-only commands: looking, listing, testing, type-checking, building. Nothing that writes, sends or installs. */
35
- const SAFE_CMD = /^(?:ls|ll|pwd|cat|head|tail|wc|echo|printf|which|type|env|printenv|date|whoami|id|uname|tree|find|fd|grep|rg|ag|awk|cut|tr|sort|uniq|diff|comm|stat|file|du|df|basename|dirname|realpath|readlink|jq|yq|xxd|hexdump|strings|test|\[|true|false|sleep|md5|shasum|sha256sum|md5sum|column|nl|less|more|open -n --args|sed -n|sed -E -n|sed -n -E|git (?:status|diff|log|show|branch|rev-parse|remote|ls-files|ls-tree|blame|describe|stash list|tag|config --get|config -l|cat-file|grep|shortlog|count-objects|reflog)|node (?:-v|--version|-e|-p|--eval|--print)|npm (?:-v|--version|ls|list|view|outdated|why|test|run (?:test|lint|typecheck|check|build|format:check))|pnpm (?:-v|--version|ls|list|why|outdated|test|typecheck|lint|build|run (?:test|lint|typecheck|check|build))|yarn (?:-v|--version|list|test|lint|typecheck|build)|npx (?:tsc|eslint|prettier --check|jest|vitest|playwright test)|tsc|eslint|prettier --check|jest|vitest|cargo (?:check|test|build|fmt --check|clippy|tree|metadata)|go (?:test|build|vet|version|list|env)|python3? (?:-V|--version|-m pytest|-c)|pytest|ruby -v|make (?:test|check|lint|build)|mvn (?:test|compile)|gradle (?:test|build)|swift (?:build|test)|xcodebuild -list|brew (?:list|info|--version)|docker (?:ps|images|version)|gh (?:pr (?:list|view|status|checks)|issue (?:list|view)|run (?:list|view)|repo view|api)|heroku (?:logs|ps|releases|config(?::get)?|apps|info))(?:\s|$)/
34
+ /**
35
+ * Read-only commands: looking, listing, testing, type-checking, building. Nothing that writes, sends
36
+ * or installs, nothing that runs code given inline (node -e, python -c, awk), nothing that prints
37
+ * secrets (heroku config).
38
+ */
39
+ /** Plain commands that only look. */
40
+ const LOOKERS = [
41
+ 'ls', 'll', 'pwd', 'cat', 'head', 'tail', 'wc', 'echo', 'printf', 'which', 'type', 'env', 'printenv',
42
+ 'date', 'whoami', 'id', 'uname', 'tree', 'find', 'fd', 'grep', 'rg', 'ag', 'cut', 'tr', 'sort', 'uniq',
43
+ 'diff', 'comm', 'stat', 'file', 'du', 'df', 'basename', 'dirname', 'realpath', 'readlink', 'jq', 'yq',
44
+ 'xxd', 'hexdump', 'strings', 'test', '[', 'true', 'false', 'sleep', 'md5', 'shasum', 'sha256sum',
45
+ 'md5sum', 'column', 'nl', 'less', 'more', 'tsc', 'eslint', 'jest', 'vitest', 'pytest',
46
+ ]
47
+ /** Tools with their read-only, test and build subcommands. */
48
+ const SUBCOMMANDS = {
49
+ git: [
50
+ 'status', 'diff', 'log', 'show', 'branch', 'rev-parse', 'remote', 'ls-files', 'ls-tree', 'blame', 'describe',
51
+ 'stash list', 'tag', 'config --get', 'config -l', 'cat-file', 'grep', 'shortlog', 'count-objects', 'reflog',
52
+ ],
53
+ node: ['-v', '--version'],
54
+ npm: ['-v', '--version', 'ls', 'list', 'view', 'outdated', 'why', 'test', 'run test', 'run lint', 'run typecheck',
55
+ 'run check', 'run build', 'run format:check'],
56
+ pnpm: ['-v', '--version', 'ls', 'list', 'why', 'outdated', 'test', 'typecheck', 'lint', 'build', 'run test',
57
+ 'run lint', 'run typecheck', 'run check', 'run build'],
58
+ yarn: ['-v', '--version', 'list', 'test', 'lint', 'typecheck', 'build'],
59
+ npx: ['tsc', 'eslint', 'prettier --check', 'jest', 'vitest', 'playwright test'],
60
+ prettier: ['--check'],
61
+ cargo: ['check', 'test', 'build', 'fmt --check', 'clippy', 'tree', 'metadata'],
62
+ go: ['test', 'build', 'vet', 'version', 'list', 'env'],
63
+ python: ['-V', '--version', '-m pytest'],
64
+ python3: ['-V', '--version', '-m pytest'],
65
+ ruby: ['-v'],
66
+ make: ['test', 'check', 'lint', 'build'],
67
+ mvn: ['test', 'compile'],
68
+ gradle: ['test', 'build'],
69
+ swift: ['build', 'test'],
70
+ xcodebuild: ['-list'],
71
+ brew: ['list', 'info', '--version'],
72
+ docker: ['ps', 'images', 'version'],
73
+ gh: ['pr list', 'pr view', 'pr status', 'pr checks', 'issue list', 'issue view', 'run list', 'run view', 'repo view'],
74
+ heroku: ['logs', 'ps', 'releases', 'apps', 'info'],
75
+ open: ['-n --args'],
76
+ sed: ['-n', '-E -n', '-n -E'],
77
+ }
78
+ const escape = w => w.replace(/[.*+?^${}()|[\]\\]/g, '\\$&')
79
+ const withSubs = Object.entries(SUBCOMMANDS).flatMap(([tool, subs]) => subs.map(sub => `${tool} ${sub}`))
80
+ const SAFE_PHRASES = [...LOOKERS, ...withSubs]
81
+ const SAFE_CMD = new RegExp(`^(?:${SAFE_PHRASES.map(escape).join('|')})(?:\\s|$)`)
36
82
 
37
83
  /** Redirections that write somewhere other than nowhere. */
38
84
  const WRITES = /(^|[^&\d])>(?!&)|>>|\btee\b|\bxargs\b|\beval\b|\bexec\b|`/
85
+ /** find and fd can run or delete what they find: then they aren't looking any more. */
86
+ const FINDER_ACTS =
87
+ /^(?:find|fd)\b.*\s(?:-exec(?:dir)?|-ok(?:dir)?|-delete|-fprint\w*|-fls|-x|--exec(?:-batch)?|-X)\b/
39
88
 
40
89
  function pieces(command) {
41
90
  // Strip quoted strings so separators inside them do not split; keep a placeholder.
@@ -76,7 +125,8 @@ export function shellIsSafe(command, allowRules = []) {
76
125
  c = c.slice(0, m.index) + '""' + c.slice(m.index + m[0].length)
77
126
  }
78
127
  if (WRITES.test(c.replace(/2>&1|2>\/dev\/null|>\/dev\/null|&>\/dev\/null/g, ''))) return false
79
- return pieces(c).every(p => SAFE_CMD.test(p) || allowRules.some(r => r && (p === r || p.startsWith(String(r) + ' '))))
128
+ const allowedByRule = p => allowRules.some(r => r && (p === r || p.startsWith(String(r) + ' ')))
129
+ return pieces(c).every(p => (SAFE_CMD.test(p) && !FINDER_ACTS.test(p)) || allowedByRule(p))
80
130
  }
81
131
 
82
132
  /** The rule key an "always allow" answer produces. */