kira-glasses-bridge 2.4.0 → 2.5.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 +1 -1
- package/src/index.mjs +5 -1
- package/src/worktrees.mjs +50 -1
package/package.json
CHANGED
package/src/index.mjs
CHANGED
|
@@ -9,7 +9,7 @@ import { agentById, AGENTS } from './agents/index.mjs'
|
|
|
9
9
|
import { normalizeAnswer } from './agents/protocol.mjs'
|
|
10
10
|
import { openApp, installedApps } from './apps.mjs'
|
|
11
11
|
import { listFiles, readUserFile, createFolder, writeUserFile, trashPath, resolveUserPath } from './files.mjs'
|
|
12
|
-
import { prepareWorktree, worktreeStatus, pushWorktree, removeWorktree, repoOfFolder } from './worktrees.mjs'
|
|
12
|
+
import { prepareWorktree, worktreeStatus, pushWorktree, removeWorktree, repoOfFolder, stageFiles } from './worktrees.mjs'
|
|
13
13
|
|
|
14
14
|
import { createRequire } from 'node:module'
|
|
15
15
|
const { version: BRIDGE_VERSION } = createRequire(import.meta.url)('../package.json')
|
|
@@ -151,6 +151,10 @@ end tell`
|
|
|
151
151
|
remove_worktree(args) {
|
|
152
152
|
return removeWorktree(args)
|
|
153
153
|
},
|
|
154
|
+
/** A task's reference files, downloaded into the worktree's .kira/files for the agent. */
|
|
155
|
+
stage_files(args) {
|
|
156
|
+
return stageFiles(args)
|
|
157
|
+
},
|
|
154
158
|
/** Which GitHub repo a folder's origin points at (project link enrichment). */
|
|
155
159
|
async repo_of_folder(args) {
|
|
156
160
|
return { repo: await repoOfFolder(args.path) }
|
package/src/worktrees.mjs
CHANGED
|
@@ -5,7 +5,10 @@
|
|
|
5
5
|
import { execFile } from 'node:child_process'
|
|
6
6
|
import { homedir } from 'node:os'
|
|
7
7
|
import { isAbsolute, join, resolve } from 'node:path'
|
|
8
|
-
import { mkdir, stat } from 'node:fs/promises'
|
|
8
|
+
import { mkdir, stat, readFile, writeFile, appendFile, rm } from 'node:fs/promises'
|
|
9
|
+
import { pipeline } from 'node:stream/promises'
|
|
10
|
+
import { createWriteStream } from 'node:fs'
|
|
11
|
+
import { Readable } from 'node:stream'
|
|
9
12
|
import { display } from './git.mjs'
|
|
10
13
|
import { resolveUserPath } from './files.mjs'
|
|
11
14
|
|
|
@@ -162,3 +165,49 @@ export async function removeWorktree({ path: p, delete_branch: deleteBranch }) {
|
|
|
162
165
|
if (deleteBranch && main && branch && branch !== 'HEAD') branchDeleted = (await git(main, ['branch', '-D', branch])).ok
|
|
163
166
|
return { removed: true, branch, branch_deleted: branchDeleted }
|
|
164
167
|
}
|
|
168
|
+
|
|
169
|
+
const STAGE_DIR = '.kira/files'
|
|
170
|
+
const STAGE_MAX_BYTES = 200 * 1024 * 1024
|
|
171
|
+
|
|
172
|
+
/**
|
|
173
|
+
* Put a task's reference files into the worktree under .kira/files (excluded
|
|
174
|
+
* from git through .git/info/exclude, never committed), so the agent can open
|
|
175
|
+
* them like anything else on disk. Downloads from short-lived signed URLs.
|
|
176
|
+
*/
|
|
177
|
+
export async function stageFiles({ path: p, files }) {
|
|
178
|
+
const path = resolveUserPath(String(p ?? ''))
|
|
179
|
+
if (!(await exists(join(path, '.git')))) return { error: 'no worktree at that path', path: display(path) }
|
|
180
|
+
const dir = join(path, STAGE_DIR)
|
|
181
|
+
await rm(dir, { recursive: true, force: true })
|
|
182
|
+
await mkdir(dir, { recursive: true })
|
|
183
|
+
// A worktree's .git is a file pointing at the real gitdir; git tells us where its exclude file lives.
|
|
184
|
+
const excl = await git(path, ['rev-parse', '--git-path', 'info/exclude'])
|
|
185
|
+
if (excl.ok) {
|
|
186
|
+
const file = isAbsolute(excl.out) ? excl.out : join(path, excl.out)
|
|
187
|
+
await mkdir(join(file, '..'), { recursive: true })
|
|
188
|
+
const current = await readFile(file, 'utf8').catch(() => '')
|
|
189
|
+
if (!current.split('\n').includes('/.kira/')) await appendFile(file, `${current && !current.endsWith('\n') ? '\n' : ''}/.kira/\n`)
|
|
190
|
+
}
|
|
191
|
+
const staged = []
|
|
192
|
+
let total = 0
|
|
193
|
+
const seen = new Set()
|
|
194
|
+
for (const f of files ?? []) {
|
|
195
|
+
let name = String(f.name ?? 'file').replace(/[\/\\]/g, '_')
|
|
196
|
+
if (seen.has(name)) name = `${name.replace(/(\.[^.]+)?$/, '')}-${seen.size}${/\.[^.]+$/.exec(name)?.[0] ?? ''}`
|
|
197
|
+
seen.add(name)
|
|
198
|
+
try {
|
|
199
|
+
const res = await fetch(String(f.url))
|
|
200
|
+
if (!res.ok || !res.body) throw new Error(`download failed (${res.status})`)
|
|
201
|
+
const len = Number(res.headers.get('content-length') ?? 0)
|
|
202
|
+
if (total + len > STAGE_MAX_BYTES) throw new Error('over the staging size limit')
|
|
203
|
+
const dest = join(dir, name)
|
|
204
|
+
await pipeline(Readable.fromWeb(res.body), createWriteStream(dest))
|
|
205
|
+
const size = (await stat(dest)).size
|
|
206
|
+
total += size
|
|
207
|
+
staged.push({ name, path: `${STAGE_DIR}/${name}`, bytes: size, description: f.description || undefined })
|
|
208
|
+
} catch (err) {
|
|
209
|
+
staged.push({ name, error: err.message })
|
|
210
|
+
}
|
|
211
|
+
}
|
|
212
|
+
return { dir: `${STAGE_DIR}/`, staged, bytes: total }
|
|
213
|
+
}
|