kira-glasses-bridge 2.20.0 → 2.21.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.20.0",
3
+ "version": "2.21.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": {
@@ -0,0 +1,29 @@
1
+ // Claude Code on this Mac, as the person signed in to it: which account it uses, and one piece
2
+ // of text written with that sign-in (complete.mjs). The bridge's calls for both.
3
+ import { execFile } from 'node:child_process'
4
+ import { completeText } from './complete.mjs'
5
+
6
+ /** Which Claude account Claude Code on this Mac is signed in to (`claude auth status --json`). */
7
+ async function claudeAccount() {
8
+ const bin = process.env.KIRA_CLAUDE_BIN || 'claude'
9
+ return new Promise(resolve =>
10
+ execFile(bin, ['auth', 'status', '--json'], { timeout: 10_000 }, (err, stdout) => {
11
+ try {
12
+ const s = JSON.parse(stdout)
13
+ resolve({
14
+ loggedIn: !!s.loggedIn,
15
+ email: s.email ?? null,
16
+ plan: s.subscriptionType ?? null,
17
+ org: s.orgName ?? null,
18
+ method: s.authMethod ?? null,
19
+ })
20
+ } catch {
21
+ const missing = err?.code === 'ENOENT'
22
+ const why = missing ? 'Claude Code is not installed on this Mac' : 'Claude Code did not say which account'
23
+ resolve({ error: why })
24
+ }
25
+ }),
26
+ )
27
+ }
28
+
29
+ export const claudeTools = { claude_account: claudeAccount, complete_text: completeText }
@@ -0,0 +1,59 @@
1
+ // One piece of text from the person's own Claude Code, with their own sign-in (no API key): a task
2
+ // brief, a recording's notes, an automation's thinking step. One turn, no tools, nothing written:
3
+ // the API asks this when their Mac is online, and uses an API key only when it isn't.
4
+ import { query } from '@anthropic-ai/claude-agent-sdk'
5
+ import { mkdirSync } from 'node:fs'
6
+ import { homedir } from 'node:os'
7
+ import { join } from 'node:path'
8
+
9
+ /** Everything Claude Code could reach for: text only here. */
10
+ const NO_TOOLS = [
11
+ 'Read', 'Write', 'Edit', 'MultiEdit', 'NotebookEdit', 'Bash', 'Glob', 'Grep', 'LS', 'WebFetch', 'WebSearch',
12
+ 'Task', 'Agent', 'TodoWrite', 'ToolSearch', 'Skill', 'ExitPlanMode', 'ListMcpResourcesTool', 'ReadMcpResourceTool',
13
+ ]
14
+ const MAX_MS = 170_000
15
+
16
+ /** Its own folder, so these one-off turns don't land among the person's sessions for real work. */
17
+ function folder() {
18
+ const dir = join(homedir(), '.kira', 'text')
19
+ mkdirSync(dir, { recursive: true })
20
+ return dir
21
+ }
22
+
23
+ export async function completeText(args) {
24
+ const system = String(args?.system ?? '').trim()
25
+ const user = String(args?.user ?? '').trim()
26
+ if (!user) return { error: 'user is required' }
27
+ const abort = new AbortController()
28
+ const timer = setTimeout(() => abort.abort(), Math.min(Number(args?.timeout_ms) || 60_000, MAX_MS))
29
+ try {
30
+ const q = query({
31
+ prompt: user,
32
+ options: {
33
+ cwd: folder(),
34
+ env: { ...process.env, ENABLE_CLAUDEAI_MCP_SERVERS: 'false' },
35
+ ...(system ? { systemPrompt: system } : {}),
36
+ disallowedTools: NO_TOOLS,
37
+ canUseTool: async () => ({ behavior: 'deny', message: 'Write the answer as text; no tools here.' }),
38
+ // Claude's best for writing that people read; the API can ask for another ("sonnet", "haiku").
39
+ model: String(args?.model ?? 'opus'),
40
+ maxTurns: 1,
41
+ settingSources: [],
42
+ abortController: abort,
43
+ ...(process.env.KIRA_CLAUDE_BIN ? { pathToClaudeCodeExecutable: process.env.KIRA_CLAUDE_BIN } : {}),
44
+ },
45
+ })
46
+ let model = null
47
+ for await (const m of q) {
48
+ if (m.type === 'system' && m.subtype === 'init') model = m.model ?? null
49
+ if (m.type !== 'result') continue
50
+ if (m.subtype !== 'success') return { error: `Claude stopped: ${m.subtype}` }
51
+ return { text: String(m.result ?? '').trim(), model }
52
+ }
53
+ return { error: 'Claude ended without an answer' }
54
+ } catch (err) {
55
+ return { error: abort.signal.aborted ? 'Claude took too long' : `could not run Claude: ${err.message}` }
56
+ } finally {
57
+ clearTimeout(timer)
58
+ }
59
+ }
package/src/index.mjs CHANGED
@@ -13,6 +13,7 @@ import { openApp, installedApps } from './apps.mjs'
13
13
  import { listFiles, readUserFile, createFolder, writeUserFile, trashPath, resolveUserPath } from './files.mjs'
14
14
  import { initTerminals, terminalCall, terminalMessage, terminalsOffline } from './terminals.mjs'
15
15
  import { gitTools } from './git-tools.mjs'
16
+ import { claudeTools } from './agents/claude-local.mjs'
16
17
 
17
18
  import { createRequire } from 'node:module'
18
19
  const { version: BRIDGE_VERSION } = createRequire(import.meta.url)('../package.json')
@@ -212,22 +213,7 @@ end tell`
212
213
  },
213
214
 
214
215
  ...gitTools,
215
-
216
- /** Which Claude account Claude Code on this Mac is signed in to (`claude auth status --json`). */
217
- async claude_account() {
218
- const { execFile } = await import('node:child_process')
219
- const bin = process.env.KIRA_CLAUDE_BIN || 'claude'
220
- return new Promise(resolve =>
221
- execFile(bin, ['auth', 'status', '--json'], { timeout: 10_000 }, (err, stdout) => {
222
- try {
223
- const s = JSON.parse(stdout)
224
- resolve({ loggedIn: !!s.loggedIn, email: s.email ?? null, plan: s.subscriptionType ?? null, org: s.orgName ?? null, method: s.authMethod ?? null })
225
- } catch {
226
- resolve({ error: err?.code === 'ENOENT' ? 'Claude Code is not installed on this Mac' : 'Claude Code did not say which account it uses' })
227
- }
228
- }),
229
- )
230
- },
216
+ ...claudeTools,
231
217
 
232
218
 
233
219
  }