@rose42/t3-feedback 0.1.0 → 0.1.2
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 +65 -50
- package/dist/bridge/http.d.ts +7 -2
- package/dist/bridge/http.js +41 -1
- package/dist/bridge/index.d.ts +5 -3
- package/dist/bridge/index.js +4 -2
- package/dist/bridge/launchEditor.d.ts +21 -0
- package/dist/bridge/launchEditor.js +139 -0
- package/dist/bridge/paths.d.ts +2 -0
- package/dist/bridge/paths.js +2 -0
- package/dist/bridge/readSource.js +2 -0
- package/dist/bridge/sendFeedback.d.ts +17 -1
- package/dist/bridge/sendFeedback.js +23 -6
- package/dist/cli/bin.js +0 -0
- package/dist/client/index.d.ts +1 -1
- package/dist/client/index.js +1 -1
- package/dist/client/mount.js +50 -12
- package/dist/client/ui/backdropAway.d.ts +2 -0
- package/dist/client/ui/backdropAway.js +12 -0
- package/dist/client/ui/elementTargetChip.d.ts +13 -4
- package/dist/client/ui/elementTargetChip.js +21 -7
- package/dist/client/ui/elementTargetPanelSession.js +6 -4
- package/dist/client/ui/screenshotPanelSession.js +36 -28
- package/dist/client/ui/sourcePathPrefix.d.ts +12 -0
- package/dist/client/ui/sourcePathPrefix.js +39 -0
- package/dist/index.d.ts +3 -3
- package/dist/index.js +2 -2
- package/dist/plugin.d.ts +6 -1
- package/dist/plugin.js +71 -4
- package/dist/types.d.ts +49 -3
- package/dist/types.js +47 -0
- package/docs/devcontainer.md +2 -0
- package/docs/release.md +47 -0
- package/package.json +22 -21
package/README.md
CHANGED
|
@@ -101,6 +101,7 @@ export default defineConfig({
|
|
|
101
101
|
// // or { key: "f", mod: true, shift: true } for Ctrl/⌘+Shift+F
|
|
102
102
|
// threadReuse: "ask", // or "session-route" | "never"
|
|
103
103
|
// threadTitle: "Checkout", // optional title prefix
|
|
104
|
+
// modelSelection: { instanceId: "cursor", model: "auto" },
|
|
104
105
|
// origin: "http://localhost:3773",
|
|
105
106
|
}),
|
|
106
107
|
],
|
|
@@ -398,25 +399,26 @@ only clear the local activity list.
|
|
|
398
399
|
|
|
399
400
|
## Options
|
|
400
401
|
|
|
401
|
-
| Option | Default | Notes
|
|
402
|
-
| -------------------- | -------------------------------------------- |
|
|
403
|
-
| `origin` | `http://localhost:3773` | Overridable via `T3_FEEDBACK_ORIGIN` / `T3_ORIGIN`
|
|
404
|
-
| `openOrigin` | same as `origin` | Host-browser URL for Open-in-T3 (`T3_FEEDBACK_OPEN_ORIGIN`). Set explicitly in multi-container setups; do not point at another project's T3.
|
|
405
|
-
| `threadReuse` | `"ask"` | `"ask"` replaces Send with New / ▶ {title} ▾: **Ctrl/⌘+Enter** soft-default, **Ctrl/⌘+Shift+Enter** New, **Alt+Enter** Continue; chevron lists project threads (search); `"session-route"` always reuses per pathname; `"never"` always creates
|
|
406
|
-
| `threadTitle` | _(none)_ | Optional prefix for new thread titles (`Bug: path · comment`)
|
|
407
|
-
| `
|
|
408
|
-
| `
|
|
409
|
-
| `
|
|
410
|
-
| `
|
|
411
|
-
| `
|
|
412
|
-
| `
|
|
413
|
-
| `
|
|
414
|
-
| `
|
|
415
|
-
| `
|
|
416
|
-
| `
|
|
417
|
-
| `
|
|
418
|
-
| `
|
|
419
|
-
| `
|
|
402
|
+
| Option | Default | Notes |
|
|
403
|
+
| -------------------- | -------------------------------------------- | ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
|
|
404
|
+
| `origin` | `http://localhost:3773` | Overridable via `T3_FEEDBACK_ORIGIN` / `T3_ORIGIN` |
|
|
405
|
+
| `openOrigin` | same as `origin` | Host-browser URL for Open-in-T3 (`T3_FEEDBACK_OPEN_ORIGIN`). Set explicitly in multi-container setups; do not point at another project's T3. |
|
|
406
|
+
| `threadReuse` | `"ask"` | `"ask"` replaces Send with New / ▶ {title} ▾: **Ctrl/⌘+Enter** soft-default, **Ctrl/⌘+Shift+Enter** New, **Alt+Enter** Continue; chevron lists project threads (search); `"session-route"` always reuses per pathname; `"never"` always creates |
|
|
407
|
+
| `threadTitle` | _(none)_ | Optional prefix for new thread titles (`Bug: path · comment`) |
|
|
408
|
+
| `modelSelection` | `{ instanceId: "cursor", model: "default" }` | Provider/model for **new** threads. When omitted, prefers an existing same-project Cursor thread (e.g. Auto) instead of copying the first thread. Env: `T3_FEEDBACK_INSTANCE_ID` / `T3_FEEDBACK_MODEL` |
|
|
409
|
+
| `workspaceRootHints` | `[cwd]` (+ `T3_FEEDBACK_WORKSPACE_HINT`) | Match T3 project by workspace path |
|
|
410
|
+
| `shortcut` | _(omit for Ctrl/⌘+Shift+F)_ | When set, omitted `mod`/`shift`/`alt` are `false` (e.g. `{ key: "f" }` = bare F). Bare keys skip editable fields; Ctrl/⌘+Shift+key still opens while typing |
|
|
411
|
+
| `shortcutKey` | _(deprecated)_ | Key-only alias; prefer `shortcut.key` |
|
|
412
|
+
| `pick` | Ctrl/⌘+Shift+E, `afterPick: "crop"` | Pick shortcut (`false` to disable), after-pick capture/crop, padding; `openInEditor` (default on) makes resolved file paths clickable via Vite `/__open-in-editor` or a `urlTemplate`; `sourcePathPrefix` makes paths workspace-relative (e.g. `examples/vite-react`) — also auto-derived by the Vite plugin when `workspaceRootHints` is an ancestor of the Vite root |
|
|
413
|
+
| `screenshot` | Ctrl/⌘+Shift+S, `crop: "ui"`, `maxImages: 5` | Capture shortcut (`false` to disable); crop `"ui"` (on-demand from thumbs) \| `"auto"` \| `"none"`; gallery cap |
|
|
414
|
+
| `record` | Ctrl/⌘+Shift+R | Record / frame-burst shortcut (`false` to disable); gated with `screenshot` in `activePlugins` |
|
|
415
|
+
| `activePlugins` | all four built-ins | `pageContext` \| `consoleLogs` \| `screenshot` \| `elementTarget` — omit for defaults |
|
|
416
|
+
| `pluginOptions` | `consoleLogs.maxEntries: 200` | Serializable options for selected built-ins |
|
|
417
|
+
| `customPlugins` | `[]` | Vite-resolved module paths/ids imported into the injected client |
|
|
418
|
+
| `injectClient` | `true` | HTML inject of the overlay |
|
|
419
|
+
| `fab` | mobile-only, bottom-right | FAB visibility / position / size |
|
|
420
|
+
| `reportTypeGlyph` | `"icon"` | `"icon"` (SVG + textual titles), `"emoji"`, or a map `{ Bug, Feedback, Suggestion }` — emoji/map also prefix new chat names |
|
|
421
|
+
| `version` | _(none)_ | App version on bug/feedback reports; DEV also attaches HMR `#n` |
|
|
420
422
|
|
|
421
423
|
## Environment
|
|
422
424
|
|
|
@@ -424,6 +426,8 @@ only clear the local activity list.
|
|
|
424
426
|
| ---------------------------------- | ----------------------------------------------------------------------------------------- |
|
|
425
427
|
| `T3_FEEDBACK_ORIGIN` / `T3_ORIGIN` | T3 HTTP origin |
|
|
426
428
|
| `T3_FEEDBACK_OPEN_ORIGIN` | Browser origin for Open-in-T3 (default: same as `T3_FEEDBACK_ORIGIN`) |
|
|
429
|
+
| `T3_FEEDBACK_INSTANCE_ID` | Default provider instance for new threads (e.g. `cursor`) |
|
|
430
|
+
| `T3_FEEDBACK_MODEL` | Default model id for new threads (e.g. `auto`, `default`) |
|
|
427
431
|
| `T3_FEEDBACK_TOKEN` | Pre-issued bearer (skips `t3 auth session issue`) |
|
|
428
432
|
| `T3_FEEDBACK_WORKSPACE_HINT` | Extra workspace path hint |
|
|
429
433
|
| `LAUNCH_EDITOR` | Editor binary for Vite `/__open-in-editor` (e.g. `cursor`, `code`) when auto-detect fails |
|
|
@@ -439,28 +443,58 @@ container avoid opening the wrong environment (check the Vite log line
|
|
|
439
443
|
`T3_FEEDBACK_TOKEN` set. Browser needs `getDisplayMedia` (paste image works as fallback).
|
|
440
444
|
|
|
441
445
|
When element pick resolves a component file, the ELEMENT chip path is a link that
|
|
442
|
-
opens the IDE. Default is
|
|
443
|
-
|
|
444
|
-
|
|
446
|
+
opens the IDE. Default is the bridge `GET {api}/open-editor?file=…` (same origin
|
|
447
|
+
as feedback posts), which launches the Cursor/VS Code **remote CLI** so
|
|
448
|
+
container `/workspaces/…` paths remap to the host. Vite’s `/__open-in-editor`
|
|
449
|
+
still works if you set `pick.openInEditor.endpoint` explicitly
|
|
450
|
+
(`LAUNCH_EDITOR=cursor` or `code` if auto-detect fails).
|
|
445
451
|
|
|
446
|
-
When the
|
|
447
|
-
|
|
452
|
+
When the Vite/Next app lives in a **subfolder** of the T3 / IDE workspace (e.g.
|
|
453
|
+
`examples/vite-react`), set `workspaceRootHints` to the workspace root so the
|
|
454
|
+
plugin prefixes chip + agent paths (`examples/vite-react/src/App.tsx`). Or set
|
|
455
|
+
`pick.sourcePathPrefix` explicitly.
|
|
456
|
+
|
|
457
|
+
```ts
|
|
458
|
+
t3FeedbackPlugin({
|
|
459
|
+
workspaceRootHints: [path.resolve(import.meta.dirname, "../..")],
|
|
460
|
+
pick: { sourceFramework: "react" },
|
|
461
|
+
});
|
|
462
|
+
```
|
|
463
|
+
|
|
464
|
+
When the **browser is on the host** and the remote CLI is unavailable, use a
|
|
465
|
+
protocol URL with a **host** filesystem root — not the container `/workspaces/…`
|
|
466
|
+
path:
|
|
448
467
|
|
|
449
468
|
```ts
|
|
450
469
|
t3FeedbackPlugin({
|
|
451
470
|
pick: {
|
|
452
471
|
openInEditor: {
|
|
453
|
-
//
|
|
454
|
-
|
|
455
|
-
urlTemplate: `cursor://file${process.cwd()}/{file}:{line}:{column}`,
|
|
472
|
+
// Prefer the host checkout path (e.g. from your bind-mount source).
|
|
473
|
+
urlTemplate: `cursor://file${process.env.HOST_WORKSPACE_ROOT}/{file}:{line}:{column}`,
|
|
456
474
|
},
|
|
457
475
|
},
|
|
458
476
|
});
|
|
459
477
|
```
|
|
460
478
|
|
|
461
|
-
With `urlTemplate`, the chip renders a real `<a href>` (not a fetch to
|
|
479
|
+
With `urlTemplate`, the chip renders a real `<a href>` (not a fetch to the bridge).
|
|
462
480
|
`vscode://file/…` also works if that scheme is bound to your editor.
|
|
463
481
|
|
|
482
|
+
## Examples
|
|
483
|
+
|
|
484
|
+
Minimal demos for each host connector live under [`examples/`](examples/) and run
|
|
485
|
+
in this same Dev Container against the existing T3 (`T3CODE_PORT`). Each example
|
|
486
|
+
declares `@rose42/t3-feedback` from npm and overrides it to `link:../..` so local
|
|
487
|
+
`dist/` is used.
|
|
488
|
+
|
|
489
|
+
```bash
|
|
490
|
+
pnpm build
|
|
491
|
+
pnpm examples:install
|
|
492
|
+
pnpm examples:vite-react # also: vite-vue / vite-svelte / vite-qwik / next / node
|
|
493
|
+
pnpm examples:all # every example in parallel
|
|
494
|
+
```
|
|
495
|
+
|
|
496
|
+
See [examples/README.md](examples/README.md) for ports and the dependency model.
|
|
497
|
+
|
|
464
498
|
## Package scripts
|
|
465
499
|
|
|
466
500
|
```bash
|
|
@@ -480,37 +514,18 @@ pnpm dev
|
|
|
480
514
|
|
|
481
515
|
## Release
|
|
482
516
|
|
|
483
|
-
Releases are driven by **version tags** (`vX.Y.Z`). Pushing a tag runs CI: verify → GitLab Release (dist tarball in the package registry) → publish to public npm.
|
|
517
|
+
Releases are driven by **version tags** (`vX.Y.Z`). Pushing a tag runs CI: verify → GitLab Release (dist tarball in the package registry) → publish to public npm via **OIDC trusted publishing** (no `NPM_TOKEN`).
|
|
484
518
|
|
|
485
519
|
CI installs `@rose42/feedback-capture@0.1.0` from npm. Local/devcontainer work
|
|
486
520
|
symlinks a sibling [`feedback-capture`](https://gitlab.com/rose42/feedback-capture) clone
|
|
487
521
|
into `node_modules` without changing the declared dependency.
|
|
488
522
|
|
|
489
|
-
|
|
490
|
-
|
|
491
|
-
1. Create or join the **`rose42`** org on [npmjs.com](https://www.npmjs.com) with permission to publish `@rose42/*`.
|
|
492
|
-
2. Create an npm **Automation** (or granular) access token with publish rights for `@rose42/t3-feedback` (or the whole `@rose42` org).
|
|
493
|
-
3. In GitLab: **Settings → CI/CD → Variables** → add:
|
|
494
|
-
- Key: `NPM_TOKEN`
|
|
495
|
-
- Value: the token
|
|
496
|
-
- Flags: **Masked**; use **Protected** only if `v*` tags are protected (otherwise leave Protected off so tag pipelines can read it).
|
|
497
|
-
4. Optional local check: `pnpm login` then `pnpm publish --dry-run --access public`.
|
|
498
|
-
|
|
499
|
-
### Cut a release
|
|
500
|
-
|
|
501
|
-
Requires a clean git worktree. Bumps `package.json`, commits, tags `vX.Y.Z`, and pushes (with `--follow-tags`) so the tag pipeline runs. Ensure `NPM_TOKEN` is set in GitLab before the first publish.
|
|
523
|
+
See [docs/release.md](docs/release.md) for trusted-publisher setup, post-publish hardening, and cutting a release:
|
|
502
524
|
|
|
503
525
|
```bash
|
|
504
|
-
pnpm release -- 0.1.
|
|
505
|
-
# later:
|
|
506
|
-
pnpm release -- patch # or: minor | major
|
|
526
|
+
pnpm release -- patch # or 0.1.1 / minor / major
|
|
507
527
|
```
|
|
508
528
|
|
|
509
|
-
After the pipeline succeeds:
|
|
510
|
-
|
|
511
|
-
- npm: [`@rose42/t3-feedback`](https://www.npmjs.com/package/@rose42/t3-feedback)
|
|
512
|
-
- GitLab: **Deploy → Releases** (asset links to the generic package tarball)
|
|
513
|
-
|
|
514
529
|
## License
|
|
515
530
|
|
|
516
531
|
MIT © Rose42
|
package/dist/bridge/http.d.ts
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
|
-
import type { T3FeedbackReportTypeGlyphResolved } from "../types.js";
|
|
2
|
-
export type T3FeedbackBridgeRoute = "feedback" | "threads" | "settle" | "open" | "source" | null;
|
|
1
|
+
import type { T3FeedbackModelSelection, T3FeedbackReportTypeGlyphResolved } from "../types.js";
|
|
2
|
+
export type T3FeedbackBridgeRoute = "feedback" | "threads" | "settle" | "open" | "open-editor" | "source" | null;
|
|
3
3
|
/**
|
|
4
4
|
* Options for the host-agnostic Fetch handler (Vite middleware, Next Route Handler, …).
|
|
5
5
|
* Env vars (`T3_FEEDBACK_*`) apply when fields are omitted — same as the Vite plugin.
|
|
@@ -14,6 +14,11 @@ export type T3FeedbackBridgeServerOptions = {
|
|
|
14
14
|
openOrigin?: string;
|
|
15
15
|
workspaceRootHints?: string[];
|
|
16
16
|
threadTitlePrefix?: string;
|
|
17
|
+
/**
|
|
18
|
+
* Provider/model for newly created threads.
|
|
19
|
+
* Env fallback: `T3_FEEDBACK_INSTANCE_ID` + `T3_FEEDBACK_MODEL`.
|
|
20
|
+
*/
|
|
21
|
+
modelSelection?: T3FeedbackModelSelection;
|
|
17
22
|
reportTypeGlyph?: T3FeedbackReportTypeGlyphResolved;
|
|
18
23
|
token?: string;
|
|
19
24
|
};
|
package/dist/bridge/http.js
CHANGED
|
@@ -1,8 +1,10 @@
|
|
|
1
1
|
import { resolveOpenOrigin } from "./format.js";
|
|
2
|
-
import {
|
|
2
|
+
import { launchEditorAt, parseEditorFileParam, resolveEditorFilePath } from "./launchEditor.js";
|
|
3
|
+
import { T3_FEEDBACK_API_PATH, T3_FEEDBACK_OPEN_EDITOR_SUFFIX, T3_FEEDBACK_OPEN_SUFFIX, T3_FEEDBACK_SETTLE_SUFFIX, T3_FEEDBACK_SOURCE_SUFFIX, T3_FEEDBACK_THREADS_SUFFIX, } from "./paths.js";
|
|
3
4
|
import { parseFeedbackPost, parseOpenHandoffRequest, parseSettlePost, parseThreadProbeIds, } from "./parse.js";
|
|
4
5
|
import { readWorkspaceSourceFile } from "./readSource.js";
|
|
5
6
|
import { createOpenHandoff, listThreadsInT3, probeThreadsInT3, sendFeedbackToT3, settleThreadsInT3, } from "./sendFeedback.js";
|
|
7
|
+
import { normalizeModelSelection } from "../types.js";
|
|
6
8
|
import path from "node:path";
|
|
7
9
|
const DEFAULT_ORIGIN = "http://localhost:3773";
|
|
8
10
|
function pathResolveUp(from, levels) {
|
|
@@ -22,6 +24,8 @@ export function matchT3FeedbackRoute(pathname, basePath = T3_FEEDBACK_API_PATH)
|
|
|
22
24
|
return "threads";
|
|
23
25
|
if (pathname === `${base}${T3_FEEDBACK_SETTLE_SUFFIX}`)
|
|
24
26
|
return "settle";
|
|
27
|
+
if (pathname === `${base}${T3_FEEDBACK_OPEN_EDITOR_SUFFIX}`)
|
|
28
|
+
return "open-editor";
|
|
25
29
|
if (pathname === `${base}${T3_FEEDBACK_OPEN_SUFFIX}`)
|
|
26
30
|
return "open";
|
|
27
31
|
if (pathname === `${base}${T3_FEEDBACK_SOURCE_SUFFIX}`)
|
|
@@ -42,12 +46,20 @@ function resolveBridgeEnv(options) {
|
|
|
42
46
|
const workspaceRootHints = options.workspaceRootHints ??
|
|
43
47
|
[process.env.T3_FEEDBACK_WORKSPACE_HINT, process.cwd()].filter((value) => Boolean(value));
|
|
44
48
|
const threadTitlePrefix = options.threadTitlePrefix?.trim() || undefined;
|
|
49
|
+
const modelSelection = normalizeModelSelection(options.modelSelection) ??
|
|
50
|
+
normalizeModelSelection({
|
|
51
|
+
...(process.env.T3_FEEDBACK_INSTANCE_ID
|
|
52
|
+
? { instanceId: process.env.T3_FEEDBACK_INSTANCE_ID }
|
|
53
|
+
: {}),
|
|
54
|
+
...(process.env.T3_FEEDBACK_MODEL ? { model: process.env.T3_FEEDBACK_MODEL } : {}),
|
|
55
|
+
});
|
|
45
56
|
const token = options.token ?? process.env.T3_FEEDBACK_TOKEN;
|
|
46
57
|
return {
|
|
47
58
|
origin,
|
|
48
59
|
openOrigin,
|
|
49
60
|
workspaceRootHints,
|
|
50
61
|
...(threadTitlePrefix ? { threadTitlePrefix } : {}),
|
|
62
|
+
...(modelSelection ? { modelSelection } : {}),
|
|
51
63
|
...(options.reportTypeGlyph ? { reportTypeGlyph: options.reportTypeGlyph } : {}),
|
|
52
64
|
...(token ? { token } : {}),
|
|
53
65
|
basePath: normalizeBasePath(options.basePath ?? T3_FEEDBACK_API_PATH),
|
|
@@ -116,6 +128,33 @@ export async function handleT3FeedbackRequest(request, options = {}) {
|
|
|
116
128
|
});
|
|
117
129
|
return jsonResponse(200, handoff);
|
|
118
130
|
}
|
|
131
|
+
if (route === "open-editor" && request.method === "GET") {
|
|
132
|
+
const fileParam = url.searchParams.get("file")?.trim() ?? "";
|
|
133
|
+
const parsed = parseEditorFileParam(fileParam);
|
|
134
|
+
if (!parsed) {
|
|
135
|
+
return jsonResponse(400, {
|
|
136
|
+
error: "Missing or invalid file query param (expected path:line:column)",
|
|
137
|
+
});
|
|
138
|
+
}
|
|
139
|
+
const hints = [
|
|
140
|
+
...resolved.workspaceRootHints,
|
|
141
|
+
process.cwd(),
|
|
142
|
+
pathResolveUp(process.cwd(), 1),
|
|
143
|
+
pathResolveUp(process.cwd(), 2),
|
|
144
|
+
].filter((value, index, all) => {
|
|
145
|
+
return Boolean(value) && all.indexOf(value) === index;
|
|
146
|
+
});
|
|
147
|
+
const absolutePath = resolveEditorFilePath(parsed.fileName, hints);
|
|
148
|
+
if (!absolutePath) {
|
|
149
|
+
return jsonResponse(404, { error: `File not found: ${parsed.fileName}` });
|
|
150
|
+
}
|
|
151
|
+
if (!launchEditorAt(absolutePath, parsed.line, parsed.column)) {
|
|
152
|
+
return jsonResponse(503, {
|
|
153
|
+
error: "No editor CLI found (set LAUNCH_EDITOR, or install Cursor/VS Code remote CLI)",
|
|
154
|
+
});
|
|
155
|
+
}
|
|
156
|
+
return new Response(null, { status: 204 });
|
|
157
|
+
}
|
|
119
158
|
if (route === "source" && request.method === "GET") {
|
|
120
159
|
const file = url.searchParams.get("file")?.trim() ?? "";
|
|
121
160
|
if (!file) {
|
|
@@ -154,6 +193,7 @@ export async function handleT3FeedbackRequest(request, options = {}) {
|
|
|
154
193
|
workspaceRootHints: resolved.workspaceRootHints,
|
|
155
194
|
...(resolved.reportTypeGlyph ? { reportTypeGlyph: resolved.reportTypeGlyph } : {}),
|
|
156
195
|
...(resolved.threadTitlePrefix ? { threadTitlePrefix: resolved.threadTitlePrefix } : {}),
|
|
196
|
+
...(resolved.modelSelection ? { modelSelection: resolved.modelSelection } : {}),
|
|
157
197
|
...(parsed.threadId ? { threadId: parsed.threadId } : {}),
|
|
158
198
|
...(resolved.token ? { token: resolved.token } : {}),
|
|
159
199
|
});
|
package/dist/bridge/index.d.ts
CHANGED
|
@@ -5,9 +5,11 @@
|
|
|
5
5
|
*/
|
|
6
6
|
export { handleT3FeedbackRequest, matchT3FeedbackRoute, type T3FeedbackBridgeRoute, type T3FeedbackBridgeServerOptions, } from "./http.js";
|
|
7
7
|
export { parseFeedbackPost, parseOpenHandoffRequest, parseSettlePost, parseThreadProbeIds, } from "./parse.js";
|
|
8
|
-
export { T3_FEEDBACK_API_PATH, T3_FEEDBACK_OPEN_PATH, T3_FEEDBACK_OPEN_SUFFIX, T3_FEEDBACK_SETTLE_PATH, T3_FEEDBACK_SETTLE_SUFFIX, T3_FEEDBACK_SOURCE_SUFFIX, T3_FEEDBACK_THREADS_PATH, T3_FEEDBACK_THREADS_SUFFIX, } from "./paths.js";
|
|
8
|
+
export { T3_FEEDBACK_API_PATH, T3_FEEDBACK_OPEN_PATH, T3_FEEDBACK_OPEN_SUFFIX, T3_FEEDBACK_OPEN_EDITOR_PATH, T3_FEEDBACK_OPEN_EDITOR_SUFFIX, T3_FEEDBACK_SETTLE_PATH, T3_FEEDBACK_SETTLE_SUFFIX, T3_FEEDBACK_SOURCE_SUFFIX, T3_FEEDBACK_THREADS_PATH, T3_FEEDBACK_THREADS_SUFFIX, } from "./paths.js";
|
|
9
9
|
export { readWorkspaceSourceFile } from "./readSource.js";
|
|
10
|
-
export {
|
|
10
|
+
export { launchEditorAt, parseEditorFileParam, resolveEditorBinary, resolveEditorFilePath, } from "./launchEditor.js";
|
|
11
|
+
export { clearT3EnvironmentIdCache, createOpenHandoff, issuePairingCredential, listThreadsInT3, parseT3JsonStdout, pickProject, probeThreadsInT3, resolveModelSelection, resolveT3Environment, resolveT3EnvironmentId, sendFeedbackToT3, settleThreadsInT3, type ProbeThreadsOptions, type ShellSnapshot, type T3BridgeOptions, type T3EnvironmentDescriptor, } from "./sendFeedback.js";
|
|
11
12
|
export { buildPairOpenUrl, buildThreadOpenUrl, deriveThreadTitle, formatCustomPluginSection, formatFeedbackMessage, normalizeOrigin, resolveOpenOrigin, type T3FeedbackFormatHeadingBody, type T3FeedbackFormatLabelValue, type T3FeedbackFormatMarkdown, } from "./format.js";
|
|
12
13
|
export { dataUrlSizeBytes, envelopeImageAttachments, isT3FeedbackAttachmentsValue, type T3FeedbackAttachmentsValue, type T3FeedbackImageAttachment, } from "./attachments.js";
|
|
13
|
-
export type { FeedbackSendResult, ThreadListItem, ThreadProbeResult, ThreadProbeState, ThreadSettleResult, ThreadsListResponse, ThreadsProbeResponse, ThreadsSettleResponse, T3FeedbackOpenHandoff, T3FeedbackReportTypeGlyphResolved, } from "../types.js";
|
|
14
|
+
export type { FeedbackSendResult, ThreadListItem, ThreadProbeResult, ThreadProbeState, ThreadSettleResult, ThreadsListResponse, ThreadsProbeResponse, ThreadsSettleResponse, T3FeedbackModelSelection, T3FeedbackOpenHandoff, T3FeedbackReportTypeGlyphResolved, } from "../types.js";
|
|
15
|
+
export { DEFAULT_MODEL_SELECTION, normalizeModelSelection } from "../types.js";
|
package/dist/bridge/index.js
CHANGED
|
@@ -5,8 +5,10 @@
|
|
|
5
5
|
*/
|
|
6
6
|
export { handleT3FeedbackRequest, matchT3FeedbackRoute, } from "./http.js";
|
|
7
7
|
export { parseFeedbackPost, parseOpenHandoffRequest, parseSettlePost, parseThreadProbeIds, } from "./parse.js";
|
|
8
|
-
export { T3_FEEDBACK_API_PATH, T3_FEEDBACK_OPEN_PATH, T3_FEEDBACK_OPEN_SUFFIX, T3_FEEDBACK_SETTLE_PATH, T3_FEEDBACK_SETTLE_SUFFIX, T3_FEEDBACK_SOURCE_SUFFIX, T3_FEEDBACK_THREADS_PATH, T3_FEEDBACK_THREADS_SUFFIX, } from "./paths.js";
|
|
8
|
+
export { T3_FEEDBACK_API_PATH, T3_FEEDBACK_OPEN_PATH, T3_FEEDBACK_OPEN_SUFFIX, T3_FEEDBACK_OPEN_EDITOR_PATH, T3_FEEDBACK_OPEN_EDITOR_SUFFIX, T3_FEEDBACK_SETTLE_PATH, T3_FEEDBACK_SETTLE_SUFFIX, T3_FEEDBACK_SOURCE_SUFFIX, T3_FEEDBACK_THREADS_PATH, T3_FEEDBACK_THREADS_SUFFIX, } from "./paths.js";
|
|
9
9
|
export { readWorkspaceSourceFile } from "./readSource.js";
|
|
10
|
-
export {
|
|
10
|
+
export { launchEditorAt, parseEditorFileParam, resolveEditorBinary, resolveEditorFilePath, } from "./launchEditor.js";
|
|
11
|
+
export { clearT3EnvironmentIdCache, createOpenHandoff, issuePairingCredential, listThreadsInT3, parseT3JsonStdout, pickProject, probeThreadsInT3, resolveModelSelection, resolveT3Environment, resolveT3EnvironmentId, sendFeedbackToT3, settleThreadsInT3, } from "./sendFeedback.js";
|
|
11
12
|
export { buildPairOpenUrl, buildThreadOpenUrl, deriveThreadTitle, formatCustomPluginSection, formatFeedbackMessage, normalizeOrigin, resolveOpenOrigin, } from "./format.js";
|
|
12
13
|
export { dataUrlSizeBytes, envelopeImageAttachments, isT3FeedbackAttachmentsValue, } from "./attachments.js";
|
|
14
|
+
export { DEFAULT_MODEL_SELECTION, normalizeModelSelection } from "../types.js";
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Locate a Cursor / VS Code remote CLI binary (or honor `LAUNCH_EDITOR`).
|
|
3
|
+
* Returns an absolute path, or `null` when nothing usable is found.
|
|
4
|
+
*/
|
|
5
|
+
export declare function resolveEditorBinary(): string | null;
|
|
6
|
+
/** Parse Vite-style `path:line:column` (column optional). */
|
|
7
|
+
export declare function parseEditorFileParam(fileParam: string): {
|
|
8
|
+
fileName: string;
|
|
9
|
+
line: number;
|
|
10
|
+
column: number;
|
|
11
|
+
} | null;
|
|
12
|
+
/**
|
|
13
|
+
* Resolve a workspace- or app-relative source path under one of the hint roots.
|
|
14
|
+
* Rejects path traversal.
|
|
15
|
+
*/
|
|
16
|
+
export declare function resolveEditorFilePath(fileName: string, workspaceRootHints: readonly string[]): string | null;
|
|
17
|
+
/**
|
|
18
|
+
* Open a file in the IDE via the remote CLI / LAUNCH_EDITOR.
|
|
19
|
+
* Returns true when a spawn was started (does not wait for the editor).
|
|
20
|
+
*/
|
|
21
|
+
export declare function launchEditorAt(absolutePath: string, line?: number, column?: number): boolean;
|
|
@@ -0,0 +1,139 @@
|
|
|
1
|
+
import { spawn } from "node:child_process";
|
|
2
|
+
import fs from "node:fs";
|
|
3
|
+
import path from "node:path";
|
|
4
|
+
const REMOTE_CLI_NAMES = ["cursor", "code"];
|
|
5
|
+
/**
|
|
6
|
+
* Locate a Cursor / VS Code remote CLI binary (or honor `LAUNCH_EDITOR`).
|
|
7
|
+
* Returns an absolute path, or `null` when nothing usable is found.
|
|
8
|
+
*/
|
|
9
|
+
export function resolveEditorBinary() {
|
|
10
|
+
const fromEnv = process.env.LAUNCH_EDITOR?.trim();
|
|
11
|
+
if (fromEnv && fs.existsSync(fromEnv))
|
|
12
|
+
return fromEnv;
|
|
13
|
+
const home = process.env.HOME?.trim();
|
|
14
|
+
if (home) {
|
|
15
|
+
for (const product of ["cursor-server", "vscode-server"]) {
|
|
16
|
+
const binRoot = path.join(home, `.${product}`, "bin");
|
|
17
|
+
if (!fs.existsSync(binRoot))
|
|
18
|
+
continue;
|
|
19
|
+
let versions = [];
|
|
20
|
+
try {
|
|
21
|
+
versions = fs.readdirSync(binRoot);
|
|
22
|
+
}
|
|
23
|
+
catch {
|
|
24
|
+
continue;
|
|
25
|
+
}
|
|
26
|
+
// Prefer newest install dir name (Cursor hashes are not strictly sortable;
|
|
27
|
+
// mtime is a better signal).
|
|
28
|
+
const ranked = versions
|
|
29
|
+
.map((name) => {
|
|
30
|
+
const cliDir = path.join(binRoot, name, "bin", "remote-cli");
|
|
31
|
+
try {
|
|
32
|
+
return { cliDir, mtime: fs.statSync(path.join(binRoot, name)).mtimeMs };
|
|
33
|
+
}
|
|
34
|
+
catch {
|
|
35
|
+
return null;
|
|
36
|
+
}
|
|
37
|
+
})
|
|
38
|
+
.filter((entry) => Boolean(entry))
|
|
39
|
+
.sort((a, b) => b.mtime - a.mtime);
|
|
40
|
+
for (const { cliDir } of ranked) {
|
|
41
|
+
for (const name of REMOTE_CLI_NAMES) {
|
|
42
|
+
const candidate = path.join(cliDir, name);
|
|
43
|
+
if (fs.existsSync(candidate))
|
|
44
|
+
return candidate;
|
|
45
|
+
}
|
|
46
|
+
}
|
|
47
|
+
}
|
|
48
|
+
}
|
|
49
|
+
for (const name of REMOTE_CLI_NAMES) {
|
|
50
|
+
const fromPath = lookupOnPath(name);
|
|
51
|
+
if (fromPath)
|
|
52
|
+
return fromPath;
|
|
53
|
+
}
|
|
54
|
+
return null;
|
|
55
|
+
}
|
|
56
|
+
function lookupOnPath(command) {
|
|
57
|
+
const pathEnv = process.env.PATH ?? "";
|
|
58
|
+
for (const dir of pathEnv.split(path.delimiter)) {
|
|
59
|
+
if (!dir)
|
|
60
|
+
continue;
|
|
61
|
+
const candidate = path.join(dir, command);
|
|
62
|
+
try {
|
|
63
|
+
if (fs.existsSync(candidate) && fs.statSync(candidate).isFile())
|
|
64
|
+
return candidate;
|
|
65
|
+
}
|
|
66
|
+
catch {
|
|
67
|
+
// continue
|
|
68
|
+
}
|
|
69
|
+
}
|
|
70
|
+
return null;
|
|
71
|
+
}
|
|
72
|
+
/** Parse Vite-style `path:line:column` (column optional). */
|
|
73
|
+
export function parseEditorFileParam(fileParam) {
|
|
74
|
+
const trimmed = fileParam.trim();
|
|
75
|
+
if (!trimmed)
|
|
76
|
+
return null;
|
|
77
|
+
// Split from the right so Windows drive letters / absolute paths stay intact.
|
|
78
|
+
const match = /^(.+?):(\d+)(?::(\d+))?$/.exec(trimmed);
|
|
79
|
+
if (!match?.[1] || !match[2]) {
|
|
80
|
+
return { fileName: trimmed, line: 1, column: 1 };
|
|
81
|
+
}
|
|
82
|
+
const line = Math.max(1, Number(match[2]));
|
|
83
|
+
const column = match[3] ? Math.max(1, Number(match[3])) : 1;
|
|
84
|
+
if (!Number.isFinite(line) || !Number.isFinite(column))
|
|
85
|
+
return null;
|
|
86
|
+
return { fileName: match[1], line, column };
|
|
87
|
+
}
|
|
88
|
+
/**
|
|
89
|
+
* Resolve a workspace- or app-relative source path under one of the hint roots.
|
|
90
|
+
* Rejects path traversal.
|
|
91
|
+
*/
|
|
92
|
+
export function resolveEditorFilePath(fileName, workspaceRootHints) {
|
|
93
|
+
const cleaned = fileName.replace(/\\/g, "/").trim();
|
|
94
|
+
if (!cleaned || cleaned.includes("\0") || cleaned.includes(".."))
|
|
95
|
+
return null;
|
|
96
|
+
if (path.isAbsolute(cleaned)) {
|
|
97
|
+
if (!fs.existsSync(cleaned))
|
|
98
|
+
return null;
|
|
99
|
+
const abs = path.resolve(cleaned);
|
|
100
|
+
const allowed = workspaceRootHints.some((root) => {
|
|
101
|
+
const rootResolved = path.resolve(root);
|
|
102
|
+
return abs === rootResolved || abs.startsWith(rootResolved + path.sep);
|
|
103
|
+
});
|
|
104
|
+
return allowed || workspaceRootHints.length === 0 ? abs : null;
|
|
105
|
+
}
|
|
106
|
+
for (const root of workspaceRootHints) {
|
|
107
|
+
const absolutePath = path.resolve(root, cleaned);
|
|
108
|
+
const rootResolved = path.resolve(root);
|
|
109
|
+
if (absolutePath !== rootResolved && !absolutePath.startsWith(rootResolved + path.sep)) {
|
|
110
|
+
continue;
|
|
111
|
+
}
|
|
112
|
+
if (fs.existsSync(absolutePath))
|
|
113
|
+
return absolutePath;
|
|
114
|
+
}
|
|
115
|
+
return null;
|
|
116
|
+
}
|
|
117
|
+
/**
|
|
118
|
+
* Open a file in the IDE via the remote CLI / LAUNCH_EDITOR.
|
|
119
|
+
* Returns true when a spawn was started (does not wait for the editor).
|
|
120
|
+
*/
|
|
121
|
+
export function launchEditorAt(absolutePath, line = 1, column = 1) {
|
|
122
|
+
const editor = resolveEditorBinary();
|
|
123
|
+
if (!editor)
|
|
124
|
+
return false;
|
|
125
|
+
const target = `${absolutePath}:${Math.max(1, line)}:${Math.max(1, column)}`;
|
|
126
|
+
try {
|
|
127
|
+
// Cursor / VS Code remote CLI: `cursor -g file:line:column`
|
|
128
|
+
const child = spawn(editor, ["-g", target], {
|
|
129
|
+
detached: true,
|
|
130
|
+
stdio: "ignore",
|
|
131
|
+
env: process.env,
|
|
132
|
+
});
|
|
133
|
+
child.unref();
|
|
134
|
+
return true;
|
|
135
|
+
}
|
|
136
|
+
catch {
|
|
137
|
+
return false;
|
|
138
|
+
}
|
|
139
|
+
}
|
package/dist/bridge/paths.d.ts
CHANGED
|
@@ -3,8 +3,10 @@ export declare const T3_FEEDBACK_API_PATH = "/__t3-feedback";
|
|
|
3
3
|
export declare const T3_FEEDBACK_THREADS_PATH = "/__t3-feedback/threads";
|
|
4
4
|
export declare const T3_FEEDBACK_SETTLE_PATH = "/__t3-feedback/settle";
|
|
5
5
|
export declare const T3_FEEDBACK_OPEN_PATH = "/__t3-feedback/open";
|
|
6
|
+
export declare const T3_FEEDBACK_OPEN_EDITOR_PATH = "/__t3-feedback/open-editor";
|
|
6
7
|
/** Suffixes under a host-chosen base path (e.g. `/api/t3-feedback`). */
|
|
7
8
|
export declare const T3_FEEDBACK_THREADS_SUFFIX = "/threads";
|
|
8
9
|
export declare const T3_FEEDBACK_SETTLE_SUFFIX = "/settle";
|
|
9
10
|
export declare const T3_FEEDBACK_OPEN_SUFFIX = "/open";
|
|
11
|
+
export declare const T3_FEEDBACK_OPEN_EDITOR_SUFFIX = "/open-editor";
|
|
10
12
|
export declare const T3_FEEDBACK_SOURCE_SUFFIX = "/source";
|
package/dist/bridge/paths.js
CHANGED
|
@@ -3,8 +3,10 @@ export const T3_FEEDBACK_API_PATH = "/__t3-feedback";
|
|
|
3
3
|
export const T3_FEEDBACK_THREADS_PATH = "/__t3-feedback/threads";
|
|
4
4
|
export const T3_FEEDBACK_SETTLE_PATH = "/__t3-feedback/settle";
|
|
5
5
|
export const T3_FEEDBACK_OPEN_PATH = "/__t3-feedback/open";
|
|
6
|
+
export const T3_FEEDBACK_OPEN_EDITOR_PATH = "/__t3-feedback/open-editor";
|
|
6
7
|
/** Suffixes under a host-chosen base path (e.g. `/api/t3-feedback`). */
|
|
7
8
|
export const T3_FEEDBACK_THREADS_SUFFIX = "/threads";
|
|
8
9
|
export const T3_FEEDBACK_SETTLE_SUFFIX = "/settle";
|
|
9
10
|
export const T3_FEEDBACK_OPEN_SUFFIX = "/open";
|
|
11
|
+
export const T3_FEEDBACK_OPEN_EDITOR_SUFFIX = "/open-editor";
|
|
10
12
|
export const T3_FEEDBACK_SOURCE_SUFFIX = "/source";
|
|
@@ -15,9 +15,11 @@ export async function readWorkspaceSourceFile(fileParam, workspaceRootHints) {
|
|
|
15
15
|
if (!ALLOWED_EXT.has(ext))
|
|
16
16
|
return null;
|
|
17
17
|
// Prefer app/package sources; allow either apps/<pkg>/… or app/… (Next app cwd).
|
|
18
|
+
// `examples/<name>/…` covers monorepo demo apps whose IDE workspace is the repo root.
|
|
18
19
|
if (!cleaned.startsWith("app/") &&
|
|
19
20
|
!cleaned.startsWith("apps/") &&
|
|
20
21
|
!cleaned.startsWith("packages/") &&
|
|
22
|
+
!cleaned.startsWith("examples/") &&
|
|
21
23
|
!cleaned.startsWith("src/")) {
|
|
22
24
|
return null;
|
|
23
25
|
}
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import type { FeedbackEnvelope } from "@rose42/feedback-capture";
|
|
2
|
-
import type { FeedbackSendResult, T3FeedbackReportTypeGlyphResolved, ThreadListItem, ThreadProbeResult, ThreadSettleResult } from "../types.js";
|
|
2
|
+
import type { FeedbackSendResult, T3FeedbackModelSelection, T3FeedbackReportTypeGlyphResolved, ThreadListItem, ThreadProbeResult, ThreadSettleResult } from "../types.js";
|
|
3
3
|
import type { T3FeedbackOpenHandoff } from "../types.js";
|
|
4
4
|
export type ShellSnapshot = {
|
|
5
5
|
snapshotSequence: number;
|
|
@@ -34,6 +34,12 @@ export type T3BridgeOptions = {
|
|
|
34
34
|
* instead of `Bug` / `Feedback` / `Suggestion`.
|
|
35
35
|
*/
|
|
36
36
|
reportTypeGlyph?: T3FeedbackReportTypeGlyphResolved;
|
|
37
|
+
/**
|
|
38
|
+
* Provider/model for newly created threads. When set, used as-is.
|
|
39
|
+
* When omitted, {@link resolveModelSelection} prefers a same-project thread
|
|
40
|
+
* matching {@link DEFAULT_MODEL_SELECTION}.instanceId.
|
|
41
|
+
*/
|
|
42
|
+
modelSelection?: T3FeedbackModelSelection;
|
|
37
43
|
/**
|
|
38
44
|
* Reuse this thread when it still exists in the project snapshot.
|
|
39
45
|
* Stale / missing ids create a new thread instead.
|
|
@@ -86,6 +92,16 @@ export declare function pickProject(projects: Array<{
|
|
|
86
92
|
title: string;
|
|
87
93
|
workspaceRoot: string;
|
|
88
94
|
};
|
|
95
|
+
/**
|
|
96
|
+
* Pick provider/model for a new thread.
|
|
97
|
+
*
|
|
98
|
+
* 1. Explicit `configured` (plugin / env) wins.
|
|
99
|
+
* 2. Else reuse a same-project thread whose `instanceId` matches the preferred
|
|
100
|
+
* default (`cursor`) so settings like Cursor Auto are preserved.
|
|
101
|
+
* 3. Else {@link DEFAULT_MODEL_SELECTION} — never copy an arbitrary first thread
|
|
102
|
+
* (that previously inherited disabled providers such as Codex).
|
|
103
|
+
*/
|
|
104
|
+
export declare function resolveModelSelection(threads: ShellSnapshot["threads"], projectId: string, configured?: T3FeedbackModelSelection): T3FeedbackModelSelection;
|
|
89
105
|
/** Authenticate to T3 and start a turn with the feedback envelope. */
|
|
90
106
|
export declare function sendFeedbackToT3(envelope: FeedbackEnvelope, options: T3BridgeOptions): Promise<FeedbackSendResult>;
|
|
91
107
|
/** List non-archived threads for the matched T3 project (continue picker). */
|
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import { spawnSync } from "node:child_process";
|
|
2
2
|
import WebSocket from "ws";
|
|
3
|
+
import { DEFAULT_MODEL_SELECTION, normalizeModelSelection } from "../types.js";
|
|
3
4
|
import { dataUrlSizeBytes, envelopeImageAttachments } from "./attachments.js";
|
|
4
5
|
import { buildPairOpenUrl, buildThreadOpenUrl, deriveThreadTitle, formatFeedbackMessage, normalizeOrigin, resolveOpenOrigin, } from "./format.js";
|
|
5
6
|
import { isShellThreadArchived, listItemStateFromShellThread } from "./probeThreads.js";
|
|
@@ -251,16 +252,32 @@ export function pickProject(projects, hints) {
|
|
|
251
252
|
return projects[0];
|
|
252
253
|
throw new Error("No T3 projects available");
|
|
253
254
|
}
|
|
255
|
+
/**
|
|
256
|
+
* Pick provider/model for a new thread.
|
|
257
|
+
*
|
|
258
|
+
* 1. Explicit `configured` (plugin / env) wins.
|
|
259
|
+
* 2. Else reuse a same-project thread whose `instanceId` matches the preferred
|
|
260
|
+
* default (`cursor`) so settings like Cursor Auto are preserved.
|
|
261
|
+
* 3. Else {@link DEFAULT_MODEL_SELECTION} — never copy an arbitrary first thread
|
|
262
|
+
* (that previously inherited disabled providers such as Codex).
|
|
263
|
+
*/
|
|
264
|
+
export function resolveModelSelection(threads, projectId, configured) {
|
|
265
|
+
const explicit = normalizeModelSelection(configured);
|
|
266
|
+
if (explicit)
|
|
267
|
+
return explicit;
|
|
268
|
+
const preferredInstance = DEFAULT_MODEL_SELECTION.instanceId;
|
|
269
|
+
const projectThreads = threads.filter((t) => t.projectId === projectId);
|
|
270
|
+
const matching = projectThreads.find((t) => t.modelSelection?.instanceId?.trim() === preferredInstance);
|
|
271
|
+
const inherited = normalizeModelSelection(matching?.modelSelection);
|
|
272
|
+
if (inherited)
|
|
273
|
+
return inherited;
|
|
274
|
+
return { ...DEFAULT_MODEL_SELECTION };
|
|
275
|
+
}
|
|
254
276
|
/** Authenticate to T3 and start a turn with the feedback envelope. */
|
|
255
277
|
export async function sendFeedbackToT3(envelope, options) {
|
|
256
278
|
return withShellClient(options, async (client, snapshot) => {
|
|
257
279
|
const project = pickProject(snapshot.projects, options.workspaceRootHints);
|
|
258
|
-
const modelSelection = snapshot.threads
|
|
259
|
-
?.modelSelection ??
|
|
260
|
-
snapshot.threads[0]?.modelSelection ?? {
|
|
261
|
-
instanceId: "cursor",
|
|
262
|
-
model: "default",
|
|
263
|
-
};
|
|
280
|
+
const modelSelection = resolveModelSelection(snapshot.threads, project.id, options.modelSelection);
|
|
264
281
|
const branch = snapshot.threads.find((t) => t.projectId === project.id)?.branch ?? null;
|
|
265
282
|
const existing = options.threadId
|
|
266
283
|
? snapshot.threads.find((t) => t.id === options.threadId && t.projectId === project.id)
|
package/dist/cli/bin.js
CHANGED
|
File without changes
|
package/dist/client/index.d.ts
CHANGED
|
@@ -21,4 +21,4 @@ export { SESSION_THREAD_TTL_MS, defaultThreadDestination, readSessionThreadId, r
|
|
|
21
21
|
export { createT3FeedbackHttpTransport, type T3FeedbackHttpTransportOptions } from "./transport.js";
|
|
22
22
|
export { T3_OPEN_WINDOW_NAME, T3_PAIR_THEN_THREAD_DELAY_MS, fetchOpenHandoff, openInT3Seamless, shouldBypassSeamlessOpen, type OpenInT3SeamlessOptions, } from "./openInT3.js";
|
|
23
23
|
export type { FeedbackSendResult, ThreadListItem, ThreadProbeResult, ThreadProbeState, ThreadsListResponse, ThreadsProbeResponse, T3BuiltinPluginId, T3FeedbackAfterPick, T3FeedbackBuiltinPluginOptions, T3FeedbackFabConfig, T3FeedbackFabOptions, T3FeedbackFabPosition, T3FeedbackFabVisibility, T3FeedbackOpenHandoff, T3FeedbackOpenInEditorConfig, T3FeedbackOpenInEditorOptions, T3FeedbackPickConfig, T3FeedbackPickOptions, T3FeedbackRecordConfig, T3FeedbackRecordOptions, T3FeedbackReportTypeGlyph, T3FeedbackReportTypeGlyphCustom, T3FeedbackReportTypeGlyphMap, T3FeedbackReportTypeGlyphResolved, T3FeedbackScreenshotConfig, T3FeedbackScreenshotCrop, T3FeedbackScreenshotOptions, T3FeedbackShortcutConfig, T3FeedbackShortcutOptions, T3FeedbackThreadReuse, } from "../types.js";
|
|
24
|
-
export { DEFAULT_OPEN_IN_EDITOR_ENDPOINT, DEFAULT_PICK_SHORTCUT, DEFAULT_RECORD_SHORTCUT, DEFAULT_REPORT_TYPE_EMOJIS, DEFAULT_REPORT_TYPE_GLYPH, DEFAULT_SCREENSHOT_SHORTCUT, DEFAULT_SHORTCUT, T3_BUILTIN_PLUGIN_IDS, formatShortcutLabel, isEditableKeyboardTarget, matchesBareKeyOverride, matchesShortcut, resolveActivePluginList, resolveActivePlugins, resolveOpenInEditorConfig, resolvePickConfig, resolveRecordConfig, resolveReportTypeGlyph, resolveScreenshotConfig, resolveShortcut, shortcutRequiresModifier, shouldOpenFromShortcut, } from "../types.js";
|
|
24
|
+
export { DEFAULT_OPEN_IN_EDITOR_ENDPOINT, DEFAULT_PICK_SHORTCUT, DEFAULT_RECORD_SHORTCUT, DEFAULT_REPORT_TYPE_EMOJIS, DEFAULT_REPORT_TYPE_GLYPH, DEFAULT_SCREENSHOT_SHORTCUT, DEFAULT_SHORTCUT, T3_BUILTIN_PLUGIN_IDS, formatShortcutLabel, isEditableKeyboardTarget, matchesBareKeyOverride, matchesShortcut, resolveActivePluginList, resolveActivePlugins, resolveOpenInEditorConfig, resolveOpenInEditorEndpointForApi, resolvePickConfig, resolveRecordConfig, resolveReportTypeGlyph, resolveScreenshotConfig, resolveShortcut, shortcutRequiresModifier, shouldOpenFromShortcut, } from "../types.js";
|