freestyle-sync 0.1.2 → 0.1.4

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.
Files changed (45) hide show
  1. package/README.md +2 -1
  2. package/{freestyle-sync.config.ts → dist/freestyle-sync.config.js} +2 -3
  3. package/dist/main.js +1319 -0
  4. package/{plugins/agent-claude/src/index.ts → dist/plugins/agent-claude/src/index.js} +32 -29
  5. package/{plugins/agent-codex/src/index.ts → dist/plugins/agent-codex/src/index.js} +13 -14
  6. package/{plugins/agent-copilot/src/index.ts → dist/plugins/agent-copilot/src/index.js} +151 -164
  7. package/{plugins/auth-aws/src/index.ts → dist/plugins/auth-aws/src/index.js} +14 -8
  8. package/{plugins/auth-azure/src/index.ts → dist/plugins/auth-azure/src/index.js} +14 -8
  9. package/dist/plugins/auth-context.js +213 -0
  10. package/{plugins/auth-docker/src/index.ts → dist/plugins/auth-docker/src/index.js} +14 -8
  11. package/{plugins/auth-env/src/index.ts → dist/plugins/auth-env/src/index.js} +11 -11
  12. package/{plugins/auth-gcloud/src/index.ts → dist/plugins/auth-gcloud/src/index.js} +14 -8
  13. package/{plugins/auth-git/src/index.ts → dist/plugins/auth-git/src/index.js} +24 -17
  14. package/{plugins/auth-github-cli/src/index.ts → dist/plugins/auth-github-cli/src/index.js} +20 -14
  15. package/{plugins/auth-npm/src/index.ts → dist/plugins/auth-npm/src/index.js} +19 -13
  16. package/{plugins/auth-ssh/src/index.ts → dist/plugins/auth-ssh/src/index.js} +19 -13
  17. package/dist/plugins/auth-yarn/src/index.js +24 -0
  18. package/{plugins/node-npm/src/index.ts → dist/plugins/node-npm/src/index.js} +6 -8
  19. package/dist/plugins/npm-native-deps.js +307 -0
  20. package/{plugins/shell-history/src/index.ts → dist/plugins/shell-history/src/index.js} +13 -12
  21. package/{plugins/vscode/src/index.ts → dist/plugins/vscode/src/index.js} +38 -40
  22. package/{src/main.ts → dist/src/main.js} +406 -463
  23. package/dist/src/plugin-api.js +6 -0
  24. package/dist/src/pushvm.config.js +36 -0
  25. package/package.json +8 -4
  26. package/PUBLISHING.md +0 -3
  27. package/plugins/agent-claude/package.json +0 -8
  28. package/plugins/agent-codex/package.json +0 -8
  29. package/plugins/agent-copilot/package.json +0 -8
  30. package/plugins/auth-aws/package.json +0 -8
  31. package/plugins/auth-azure/package.json +0 -8
  32. package/plugins/auth-docker/package.json +0 -8
  33. package/plugins/auth-env/package.json +0 -8
  34. package/plugins/auth-gcloud/package.json +0 -8
  35. package/plugins/auth-git/package.json +0 -8
  36. package/plugins/auth-github-cli/package.json +0 -8
  37. package/plugins/auth-npm/package.json +0 -8
  38. package/plugins/auth-ssh/package.json +0 -8
  39. package/plugins/auth-yarn/package.json +0 -8
  40. package/plugins/auth-yarn/src/index.ts +0 -19
  41. package/plugins/node-npm/package.json +0 -8
  42. package/plugins/shell-history/package.json +0 -8
  43. package/plugins/vscode/package.json +0 -8
  44. package/src/plugin-api.ts +0 -107
  45. package/tsconfig.json +0 -18
@@ -4,18 +4,15 @@ import path from "node:path";
4
4
  import { fileURLToPath } from "node:url";
5
5
  import { execFile } from "node:child_process";
6
6
  import { promisify } from "node:util";
7
- import { definePlugin, type ContextCandidate, type PushvmPluginUtils, type RemoteVm } from "../../../src/plugin-api.ts";
8
-
7
+ import { definePlugin } from "../../../src/plugin-api.js";
9
8
  const execFileAsync = promisify(execFile);
10
-
11
- type CopyResult = { status: "copied"; count: number } | { status: "pending" | "skipped"; count: 0; reason?: string };
12
-
13
9
  export function copilotAgentPlugin() {
14
10
  return definePlugin({
15
11
  name: "@freestyle-sync/agent-copilot",
16
12
  async discoverContextCandidates({ options }) {
17
- if (!options.includeAgentContext) return [];
18
- const candidates: ContextCandidate[] = [];
13
+ if (!options.includeAgentContext)
14
+ return [];
15
+ const candidates = [];
19
16
  for (const codeRoot of codeUserRoots(homedir())) {
20
17
  await addCopilotCandidates(candidates, codeRoot, options.projectRoot, options.includeAllCopilotWorkspaces);
21
18
  }
@@ -29,29 +26,31 @@ export function copilotAgentPlugin() {
29
26
  if (result.status === "copied") {
30
27
  return [`Copilot sidebar state: copied to ${result.count} VS Code Remote workspace${result.count === 1 ? "" : "s"}`];
31
28
  }
32
- if (result.status === "pending") return [`Copilot sidebar state: ${result.reason}`];
29
+ if (result.status === "pending")
30
+ return [`Copilot sidebar state: ${result.reason}`];
33
31
  return [];
34
32
  },
35
33
  async beforeOpenRemoteEditor({ vm, vmId, scheme, remoteWorkspaceUri, contextCandidates, utils }) {
34
+ if (!isSupportedEditorScheme(scheme))
35
+ return [];
36
36
  const result = await preseedLocalCopilotForRemoteUri(vm, vmId, contextCandidates, scheme, remoteWorkspaceUri, utils);
37
- if (result.status === "copied") return [`Prepared Copilot sidebar state for ${scheme === "cursor" ? "Cursor" : "VS Code"}.`];
37
+ if (result.status === "copied")
38
+ return [`Prepared Copilot sidebar state for ${scheme === "cursor" ? "Cursor" : "VS Code"}.`];
38
39
  return [];
39
40
  },
40
41
  async afterOpenRemoteEditor({ vm, vmId, scheme, remoteWorkspaceUri, contextCandidates, utils }) {
42
+ if (!isSupportedEditorScheme(scheme))
43
+ return [];
41
44
  const result = await syncLocalCopilotForRemoteUri(vm, vmId, contextCandidates, remoteWorkspaceUri, utils);
42
- if (result.status === "copied") return [`Copied Copilot sidebar state to opened ${scheme === "cursor" ? "Cursor" : "VS Code"} workspace.`];
43
- if (result.status === "pending") return ["Copilot sidebar state: opened the editor, but the workspace storage bucket was not created yet. Reload the window after it finishes connecting, then rerun vmpush if needed."];
45
+ if (result.status === "copied")
46
+ return [`Copied Copilot sidebar state to opened ${scheme === "cursor" ? "Cursor" : "VS Code"} workspace.`];
47
+ if (result.status === "pending")
48
+ return ["Copilot sidebar state: opened the editor, but the workspace storage bucket was not created yet. Reload the window after it finishes connecting, then rerun vmpush if needed."];
44
49
  return [];
45
50
  },
46
51
  });
47
52
  }
48
-
49
- async function installAgentCli(
50
- vm: RemoteVm,
51
- checkedExec: PushvmPluginUtils["checkedExec"],
52
- binary: string,
53
- packageName: string,
54
- ) {
53
+ async function installAgentCli(vm, checkedExec, binary, packageName) {
55
54
  await checkedExec(vm, `
56
55
  set -e
57
56
  export HOME="\${HOME:-/root}"
@@ -92,16 +91,14 @@ fi
92
91
  test -x /usr/local/bin/${binary}
93
92
  `, 600000);
94
93
  }
95
-
96
- async function addCopilotCandidates(candidates: ContextCandidate[], codeUserRoot: string, projectRoot: string, includeAllWorkspaces: boolean) {
97
- if (!(await exists(codeUserRoot))) return;
98
-
94
+ async function addCopilotCandidates(candidates, codeUserRoot, projectRoot, includeAllWorkspaces) {
95
+ if (!(await exists(codeUserRoot)))
96
+ return;
99
97
  const remoteUserRoot = remoteCodeRoot(codeUserRoot);
100
98
  const globalStorage = path.join(codeUserRoot, "globalStorage", "github.copilot-chat");
101
99
  if (await exists(globalStorage)) {
102
100
  addCandidate(candidates, globalStorage, `${remoteUserRoot}/globalStorage/github.copilot-chat`, "Copilot global chat state", "context:copilot-global");
103
101
  }
104
-
105
102
  const workspaceStorage = path.join(codeUserRoot, "workspaceStorage");
106
103
  const workspaceDirs = await listDirectories(workspaceStorage);
107
104
  for (const workspaceDir of workspaceDirs) {
@@ -115,13 +112,11 @@ async function addCopilotCandidates(candidates: ContextCandidate[], codeUserRoot
115
112
  }
116
113
  }
117
114
  }
118
-
119
- async function syncLocalCopilotForRemoteWorkspaces(vm: RemoteVm, vmId: string, remoteProjectDir: string, contextCandidates: ContextCandidate[], utils: PushvmPluginUtils): Promise<CopyResult> {
115
+ async function syncLocalCopilotForRemoteWorkspaces(vm, vmId, remoteProjectDir, contextCandidates, utils) {
120
116
  const sourceCopilotDir = selectedCurrentCopilotSource(contextCandidates);
121
117
  if (!sourceCopilotDir || !(await exists(sourceCopilotDir))) {
122
118
  return { status: "skipped", count: 0, reason: "Copilot current workspace state is not selected." };
123
119
  }
124
-
125
120
  const destinations = await findRemoteWorkspaceCopilotDestinations(vmId, remoteProjectDir);
126
121
  if (destinations.length === 0) {
127
122
  return {
@@ -130,19 +125,17 @@ async function syncLocalCopilotForRemoteWorkspaces(vm: RemoteVm, vmId: string, r
130
125
  reason: "open this VM folder in VS Code once, then rerun vmpush to copy sidebar history.",
131
126
  };
132
127
  }
133
-
134
128
  await copyCopilotState(sourceCopilotDir, destinations);
135
129
  await copyCopilotWorkspaceStateKeys(sourceCopilotDir, destinations);
136
130
  await copyCopilotStateToRemoteServer(vm, vmId, sourceCopilotDir, destinations, utils);
137
131
  return { status: "copied", count: destinations.length };
138
132
  }
139
-
140
- async function syncLocalCopilotForRemoteUri(vm: RemoteVm, vmId: string, contextCandidates: ContextCandidate[], remoteWorkspaceUri: string, utils: PushvmPluginUtils): Promise<CopyResult> {
133
+ async function syncLocalCopilotForRemoteUri(vm, vmId, contextCandidates, remoteWorkspaceUri, utils) {
141
134
  const sourceCopilotDir = selectedCurrentCopilotSource(contextCandidates);
142
- if (!sourceCopilotDir || !(await exists(sourceCopilotDir))) return { status: "skipped", count: 0 };
143
-
135
+ if (!sourceCopilotDir || !(await exists(sourceCopilotDir)))
136
+ return { status: "skipped", count: 0 };
144
137
  for (let attempt = 0; attempt < 20; attempt += 1) {
145
- const destinations: string[] = [];
138
+ const destinations = [];
146
139
  for (const codeRoot of codeUserRoots(homedir())) {
147
140
  const workspaceStorage = path.join(codeRoot, "workspaceStorage");
148
141
  const workspaceDirs = await listDirectories(workspaceStorage);
@@ -152,26 +145,22 @@ async function syncLocalCopilotForRemoteUri(vm: RemoteVm, vmId: string, contextC
152
145
  }
153
146
  }
154
147
  }
155
-
156
148
  if (destinations.length > 0) {
157
149
  await copyCopilotState(sourceCopilotDir, destinations);
158
150
  await copyCopilotWorkspaceStateKeys(sourceCopilotDir, destinations);
159
151
  await copyCopilotStateToRemoteServer(vm, vmId, sourceCopilotDir, destinations, utils);
160
152
  return { status: "copied", count: destinations.length };
161
153
  }
162
-
163
154
  await utils.delay(500);
164
155
  }
165
-
166
156
  return { status: "pending", count: 0 };
167
157
  }
168
-
169
- async function preseedLocalCopilotForRemoteUri(vm: RemoteVm, vmId: string, contextCandidates: ContextCandidate[], scheme: "vscode" | "cursor", remoteWorkspaceUri: string, utils: PushvmPluginUtils): Promise<CopyResult> {
158
+ async function preseedLocalCopilotForRemoteUri(vm, vmId, contextCandidates, scheme, remoteWorkspaceUri, utils) {
170
159
  const sourceCopilotDir = selectedCurrentCopilotSource(contextCandidates);
171
- if (!sourceCopilotDir || !(await exists(sourceCopilotDir))) return { status: "skipped", count: 0 };
172
-
160
+ if (!sourceCopilotDir || !(await exists(sourceCopilotDir)))
161
+ return { status: "skipped", count: 0 };
173
162
  const workspaceId = utils.md5(remoteWorkspaceUri);
174
- const destinations: string[] = [];
163
+ const destinations = [];
175
164
  for (const codeRoot of codeUserRootsForScheme(homedir(), scheme)) {
176
165
  const workspaceDir = path.join(codeRoot, "workspaceStorage", workspaceId);
177
166
  await mkdir(workspaceDir, { recursive: true });
@@ -179,19 +168,16 @@ async function preseedLocalCopilotForRemoteUri(vm: RemoteVm, vmId: string, conte
179
168
  await ensureWorkspaceStateDb(path.join(workspaceDir, "state.vscdb"));
180
169
  destinations.push(path.join(workspaceDir, "GitHub.copilot-chat"));
181
170
  }
182
-
183
171
  await copyCopilotState(sourceCopilotDir, destinations);
184
172
  await copyCopilotWorkspaceStateKeys(sourceCopilotDir, destinations);
185
173
  await copyCopilotStateToRemoteServer(vm, vmId, sourceCopilotDir, destinations, utils);
186
174
  return { status: "copied", count: destinations.length };
187
175
  }
188
-
189
- function selectedCurrentCopilotSource(contextCandidates: ContextCandidate[]) {
176
+ function selectedCurrentCopilotSource(contextCandidates) {
190
177
  return contextCandidates.find((candidate) => candidate.preferenceKey === "context:copilot-current-workspace")?.source;
191
178
  }
192
-
193
- async function findRemoteWorkspaceCopilotDestinations(vmId: string, remoteProjectDir: string) {
194
- const destinations: string[] = [];
179
+ async function findRemoteWorkspaceCopilotDestinations(vmId, remoteProjectDir) {
180
+ const destinations = [];
195
181
  for (const codeRoot of codeUserRoots(homedir())) {
196
182
  const workspaceStorage = path.join(codeRoot, "workspaceStorage");
197
183
  const workspaceDirs = await listDirectories(workspaceStorage);
@@ -203,8 +189,7 @@ async function findRemoteWorkspaceCopilotDestinations(vmId: string, remoteProjec
203
189
  }
204
190
  return destinations;
205
191
  }
206
-
207
- async function copyCopilotState(sourceCopilotDir: string, destinations: string[]) {
192
+ async function copyCopilotState(sourceCopilotDir, destinations) {
208
193
  for (const destination of destinations) {
209
194
  await rm(destination, { recursive: true, force: true });
210
195
  await mkdir(path.dirname(destination), { recursive: true });
@@ -213,8 +198,7 @@ async function copyCopilotState(sourceCopilotDir: string, destinations: string[]
213
198
  await copyCoreChatState(sourceCopilotDir, destination);
214
199
  }
215
200
  }
216
-
217
- async function copyCoreChatState(sourceCopilotDir: string, destinationCopilotDir: string) {
201
+ async function copyCoreChatState(sourceCopilotDir, destinationCopilotDir) {
218
202
  const sourceWorkspaceDir = path.dirname(sourceCopilotDir);
219
203
  const destinationWorkspaceDir = path.dirname(destinationCopilotDir);
220
204
  const sessionIds = await copiedCopilotSessionIds(destinationCopilotDir);
@@ -226,7 +210,6 @@ async function copyCoreChatState(sourceCopilotDir: string, destinationCopilotDir
226
210
  await cp(sourceChatSession, destinationChatSession, { force: true });
227
211
  await trimTranscriptToCompletedTurn(destinationChatSession);
228
212
  }
229
-
230
213
  const sourceEditingSession = path.join(sourceWorkspaceDir, "chatEditingSessions", sessionId);
231
214
  if (await exists(sourceEditingSession)) {
232
215
  const destinationEditingSession = path.join(destinationWorkspaceDir, "chatEditingSessions", sessionId);
@@ -236,29 +219,29 @@ async function copyCoreChatState(sourceCopilotDir: string, destinationCopilotDir
236
219
  }
237
220
  }
238
221
  }
239
-
240
- async function copiedCopilotSessionIds(copilotDir: string) {
222
+ async function copiedCopilotSessionIds(copilotDir) {
241
223
  const transcripts = await listFiles(path.join(copilotDir, "transcripts"));
242
224
  return transcripts.filter((transcript) => transcript.endsWith(".jsonl")).map((transcript) => path.basename(transcript, ".jsonl"));
243
225
  }
244
-
245
- async function sanitizeCopilotTranscripts(copilotDir: string) {
226
+ async function sanitizeCopilotTranscripts(copilotDir) {
246
227
  const entries = await listFiles(path.join(copilotDir, "transcripts"));
247
228
  for (const transcript of entries.filter((entry) => entry.endsWith(".jsonl"))) {
248
229
  await trimTranscriptToCompletedTurn(transcript);
249
230
  }
250
231
  }
251
-
252
- async function trimTranscriptToCompletedTurn(transcriptPath: string) {
232
+ async function trimTranscriptToCompletedTurn(transcriptPath) {
253
233
  const content = await readFile(transcriptPath, "utf8").catch(() => undefined);
254
- if (!content) return;
234
+ if (!content)
235
+ return;
255
236
  const lines = content.trimEnd().split("\n");
256
237
  let lastCompletedLine = -1;
257
238
  for (let index = 0; index < lines.length; index += 1) {
258
239
  try {
259
- const event = JSON.parse(lines[index]) as { type?: string };
260
- if (event.type === "assistant.turn_end") lastCompletedLine = index;
261
- } catch {
240
+ const event = JSON.parse(lines[index]);
241
+ if (event.type === "assistant.turn_end")
242
+ lastCompletedLine = index;
243
+ }
244
+ catch {
262
245
  break;
263
246
  }
264
247
  }
@@ -266,14 +249,14 @@ async function trimTranscriptToCompletedTurn(transcriptPath: string) {
266
249
  await writeFile(transcriptPath, `${lines.slice(0, lastCompletedLine + 1).join("\n")}\n`, "utf8");
267
250
  }
268
251
  }
269
-
270
- async function copyCopilotWorkspaceStateKeys(sourceCopilotDir: string, destinations: string[]) {
252
+ async function copyCopilotWorkspaceStateKeys(sourceCopilotDir, destinations) {
271
253
  const sourceStateDb = path.join(path.dirname(sourceCopilotDir), "state.vscdb");
272
- if (!(await exists(sourceStateDb))) return;
273
-
254
+ if (!(await exists(sourceStateDb)))
255
+ return;
274
256
  for (const destination of destinations) {
275
257
  const destinationStateDb = path.join(path.dirname(destination), "state.vscdb");
276
- if (!(await exists(destinationStateDb))) continue;
258
+ if (!(await exists(destinationStateDb)))
259
+ continue;
277
260
  await mergeWorkspaceStateKeys(sourceStateDb, destinationStateDb).catch((error) => {
278
261
  console.warn(`Copilot sidebar state database merge skipped: ${error instanceof Error ? error.message : String(error)}`);
279
262
  });
@@ -282,8 +265,7 @@ async function copyCopilotWorkspaceStateKeys(sourceCopilotDir: string, destinati
282
265
  });
283
266
  }
284
267
  }
285
-
286
- async function mergeWorkspaceStateKeys(sourceStateDb: string, destinationStateDb: string) {
268
+ async function mergeWorkspaceStateKeys(sourceStateDb, destinationStateDb) {
287
269
  await execFileAsync("sqlite3", [
288
270
  destinationStateDb,
289
271
  `PRAGMA busy_timeout=5000;
@@ -300,17 +282,16 @@ WHERE key IN (
300
282
  DETACH DATABASE source_state;`,
301
283
  ]);
302
284
  }
303
-
304
- async function normalizeCopiedChatIndex(copilotDir: string) {
285
+ async function normalizeCopiedChatIndex(copilotDir) {
305
286
  const stateDb = path.join(path.dirname(copilotDir), "state.vscdb");
306
- if (!(await exists(stateDb))) return;
307
-
287
+ if (!(await exists(stateDb)))
288
+ return;
308
289
  const { stdout } = await execFileAsync("sqlite3", [stateDb, "SELECT value FROM ItemTable WHERE key='chat.ChatSessionStore.index';"]);
309
- if (!stdout.trim()) return;
310
-
311
- const index = JSON.parse(stdout) as { entries?: Record<string, Record<string, unknown>> };
312
- if (!index.entries) return;
313
-
290
+ if (!stdout.trim())
291
+ return;
292
+ const index = JSON.parse(stdout);
293
+ if (!index.entries)
294
+ return;
314
295
  for (const sessionId of Object.keys(index.entries)) {
315
296
  const transcriptPath = path.join(copilotDir, "transcripts", `${sessionId}.jsonl`);
316
297
  const lastCompletedTimestamp = await lastCompletedTurnTimestamp(transcriptPath);
@@ -322,36 +303,36 @@ async function normalizeCopiedChatIndex(copilotDir: string) {
322
303
  entry.lastResponseState = 1;
323
304
  entry.hasPendingEdits = false;
324
305
  entry.lastMessageDate = lastCompletedTimestamp;
325
- const timing = typeof entry.timing === "object" && entry.timing !== null ? entry.timing as Record<string, unknown> : {};
306
+ const timing = typeof entry.timing === "object" && entry.timing !== null ? entry.timing : {};
326
307
  timing.lastRequestEnded = lastCompletedTimestamp;
327
308
  entry.timing = timing;
328
309
  }
329
-
330
310
  await execFileAsync("sqlite3", [
331
311
  stateDb,
332
312
  `INSERT OR REPLACE INTO ItemTable(key, value) VALUES ('chat.ChatSessionStore.index', ${sqliteLiteral(JSON.stringify(index))});`,
333
313
  ]);
334
314
  }
335
-
336
- async function lastCompletedTurnTimestamp(transcriptPath: string) {
315
+ async function lastCompletedTurnTimestamp(transcriptPath) {
337
316
  const content = await readFile(transcriptPath, "utf8").catch(() => undefined);
338
- if (!content) return undefined;
317
+ if (!content)
318
+ return undefined;
339
319
  const lines = content.trimEnd().split("\n");
340
320
  for (let index = lines.length - 1; index >= 0; index -= 1) {
341
321
  try {
342
- const event = JSON.parse(lines[index]) as { type?: string; timestamp?: string };
343
- if (event.type === "assistant.turn_end" && event.timestamp) return Date.parse(event.timestamp);
344
- } catch {
322
+ const event = JSON.parse(lines[index]);
323
+ if (event.type === "assistant.turn_end" && event.timestamp)
324
+ return Date.parse(event.timestamp);
325
+ }
326
+ catch {
345
327
  return undefined;
346
328
  }
347
329
  }
348
330
  return undefined;
349
331
  }
350
-
351
- async function copyCopilotStateToRemoteServer(vm: RemoteVm, vmId: string, sourceCopilotDir: string, localDestinations: string[], utils: PushvmPluginUtils) {
332
+ async function copyCopilotStateToRemoteServer(vm, vmId, sourceCopilotDir, localDestinations, utils) {
352
333
  const workspaceIds = localDestinations.map((destination) => path.basename(path.dirname(destination)));
353
- if (workspaceIds.length === 0) return;
354
-
334
+ if (workspaceIds.length === 0)
335
+ return;
355
336
  const archive = await createRemoteServerWorkspaceStateArchive(sourceCopilotDir, localDestinations, utils);
356
337
  try {
357
338
  await utils.uploadArchiveInChunks(vm, vmId, archive, "/tmp/vmpush-copilot-chat.tgz", "copilot-chat");
@@ -363,17 +344,16 @@ async function copyCopilotStateToRemoteServer(vm: RemoteVm, vmId: string, source
363
344
  commands.push(`tar --no-same-owner --no-same-permissions -xzf /tmp/vmpush-copilot-chat.tgz -C ${utils.shellQuote(baseDir)}`);
364
345
  commands.push("rm -f /tmp/vmpush-copilot-chat.tgz");
365
346
  await utils.checkedExec(vm, commands.join("\n"));
366
- } finally {
347
+ }
348
+ finally {
367
349
  await rm(path.dirname(archive), { recursive: true, force: true });
368
350
  }
369
351
  }
370
-
371
- async function createRemoteServerWorkspaceStateArchive(sourceCopilotDir: string, localDestinations: string[], utils: PushvmPluginUtils) {
352
+ async function createRemoteServerWorkspaceStateArchive(sourceCopilotDir, localDestinations, utils) {
372
353
  const tempDir = await mkdtemp(path.join(tmpdir(), "vmpush-copilot-"));
373
354
  const stagingDir = path.join(tempDir, "staging");
374
355
  const archivePath = path.join(tempDir, "copilot-chat.tgz");
375
356
  await mkdir(stagingDir, { recursive: true });
376
-
377
357
  for (const destination of localDestinations) {
378
358
  const workspaceId = path.basename(path.dirname(destination));
379
359
  const workspaceDir = path.join(stagingDir, workspaceId);
@@ -382,93 +362,99 @@ async function createRemoteServerWorkspaceStateArchive(sourceCopilotDir: string,
382
362
  await sanitizeCopilotTranscripts(path.join(workspaceDir, "GitHub.copilot-chat"));
383
363
  await copyCoreChatState(sourceCopilotDir, path.join(workspaceDir, "GitHub.copilot-chat"));
384
364
  const stateDb = path.join(path.dirname(destination), "state.vscdb");
385
- if (await exists(stateDb)) await cp(stateDb, path.join(workspaceDir, "state.vscdb"), { force: true });
365
+ if (await exists(stateDb))
366
+ await cp(stateDb, path.join(workspaceDir, "state.vscdb"), { force: true });
386
367
  }
387
-
388
368
  await utils.createTar(["--no-xattrs", "-czf", archivePath, "-C", stagingDir, "."]);
389
369
  return archivePath;
390
370
  }
391
-
392
- async function isRemoteWorkspaceStorageForVmProject(workspaceDir: string, vmId: string, remoteProjectDir: string) {
371
+ async function isRemoteWorkspaceStorageForVmProject(workspaceDir, vmId, remoteProjectDir) {
393
372
  try {
394
373
  const raw = await readFile(path.join(workspaceDir, "workspace.json"), "utf8");
395
- const parsed = JSON.parse(raw) as { folder?: string; workspace?: string };
374
+ const parsed = JSON.parse(raw);
396
375
  const workspaceUri = parsed.folder ?? parsed.workspace;
397
- if (!workspaceUri?.startsWith("vscode-remote://")) return false;
376
+ if (!workspaceUri?.startsWith("vscode-remote://"))
377
+ return false;
398
378
  const decodedUri = decodeURIComponent(workspaceUri);
399
379
  return decodedUri.includes(vmId) && remoteWorkspacePath(decodedUri) === remoteProjectDir;
400
- } catch {
380
+ }
381
+ catch {
401
382
  return false;
402
383
  }
403
384
  }
404
-
405
- async function isWorkspaceStorageForRemoteUri(workspaceDir: string, remoteWorkspaceUri: string) {
385
+ async function isWorkspaceStorageForRemoteUri(workspaceDir, remoteWorkspaceUri) {
406
386
  try {
407
387
  const raw = await readFile(path.join(workspaceDir, "workspace.json"), "utf8");
408
- const parsed = JSON.parse(raw) as { folder?: string; workspace?: string };
388
+ const parsed = JSON.parse(raw);
409
389
  const workspaceUri = parsed.folder ?? parsed.workspace;
410
390
  return workspaceUri === remoteWorkspaceUri;
411
- } catch {
391
+ }
392
+ catch {
412
393
  return false;
413
394
  }
414
395
  }
415
-
416
- async function isWorkspaceStorageForProject(workspaceDir: string, projectRoot: string) {
396
+ async function isWorkspaceStorageForProject(workspaceDir, projectRoot) {
417
397
  try {
418
398
  const raw = await readFile(path.join(workspaceDir, "workspace.json"), "utf8");
419
- const parsed = JSON.parse(raw) as { folder?: string; workspace?: string };
399
+ const parsed = JSON.parse(raw);
420
400
  const workspacePath = workspaceJsonPath(parsed.folder ?? parsed.workspace);
421
401
  return workspacePath !== undefined && isSameOrParentPath(workspacePath, projectRoot);
422
- } catch {
402
+ }
403
+ catch {
423
404
  return false;
424
405
  }
425
406
  }
426
-
427
- function workspaceJsonPath(uri: string | undefined) {
428
- if (!uri) return undefined;
407
+ function workspaceJsonPath(uri) {
408
+ if (!uri)
409
+ return undefined;
429
410
  try {
430
- if (uri.startsWith("file://")) return path.resolve(fileURLToPath(uri));
411
+ if (uri.startsWith("file://"))
412
+ return path.resolve(fileURLToPath(uri));
431
413
  return path.resolve(uri);
432
- } catch {
414
+ }
415
+ catch {
433
416
  return undefined;
434
417
  }
435
418
  }
436
-
437
- function isSameOrParentPath(workspacePath: string, projectRoot: string) {
419
+ function isSameOrParentPath(workspacePath, projectRoot) {
438
420
  const relative = path.relative(workspacePath, projectRoot);
439
421
  return relative === "" || (!relative.startsWith("..") && !path.isAbsolute(relative));
440
422
  }
441
-
442
- function remoteWorkspacePath(decodedRemoteUri: string) {
423
+ function remoteWorkspacePath(decodedRemoteUri) {
443
424
  const hostEnd = decodedRemoteUri.indexOf("/", "vscode-remote://".length);
444
- if (hostEnd === -1) return "/";
425
+ if (hostEnd === -1)
426
+ return "/";
445
427
  return normalizeRemotePath(decodedRemoteUri.slice(hostEnd));
446
428
  }
447
-
448
- function normalizeRemotePath(value: string) {
429
+ function normalizeRemotePath(value) {
449
430
  const normalized = path.posix.normalize(value.replace(/\\/g, "/"));
450
- if (!normalized.startsWith("/")) return "/";
431
+ if (!normalized.startsWith("/"))
432
+ return "/";
451
433
  return normalized;
452
434
  }
453
-
454
- async function ensureWorkspaceStateDb(stateDb: string) {
435
+ async function ensureWorkspaceStateDb(stateDb) {
455
436
  await execFileAsync("sqlite3", [stateDb, "CREATE TABLE IF NOT EXISTS ItemTable (key TEXT UNIQUE ON CONFLICT REPLACE, value BLOB);"]);
456
437
  }
457
-
458
- function codeUserRootsForScheme(home: string, scheme: "vscode" | "cursor") {
459
- if (scheme === "cursor") {
460
- return [
461
- path.join(home, "Library", "Application Support", "Cursor", "User"),
462
- path.join(home, ".config", "Cursor", "User"),
463
- ];
438
+ function codeUserRootsForScheme(home, scheme) {
439
+ switch (scheme) {
440
+ case "cursor":
441
+ return [
442
+ path.join(home, "Library", "Application Support", "Cursor", "User"),
443
+ path.join(home, ".config", "Cursor", "User"),
444
+ ];
445
+ case "vscode":
446
+ return [
447
+ path.join(home, "Library", "Application Support", "Code", "User"),
448
+ path.join(home, ".config", "Code", "User"),
449
+ ];
450
+ default:
451
+ return [];
464
452
  }
465
- return [
466
- path.join(home, "Library", "Application Support", "Code", "User"),
467
- path.join(home, ".config", "Code", "User"),
468
- ];
469
453
  }
470
-
471
- function codeUserRoots(home: string) {
454
+ function isSupportedEditorScheme(scheme) {
455
+ return scheme === "vscode" || scheme === "cursor";
456
+ }
457
+ function codeUserRoots(home) {
472
458
  return [
473
459
  path.join(home, "Library", "Application Support", "Code", "User"),
474
460
  path.join(home, "Library", "Application Support", "Code - Insiders", "User"),
@@ -478,65 +464,66 @@ function codeUserRoots(home: string) {
478
464
  path.join(home, ".config", "Cursor", "User"),
479
465
  ];
480
466
  }
481
-
482
- function remoteCodeRoot(localCodeUserRoot: string) {
483
- if (localCodeUserRoot.includes("Cursor")) return "~/.config/Cursor/User";
484
- if (localCodeUserRoot.includes("Code - Insiders")) return "~/.config/Code - Insiders/User";
467
+ function remoteCodeRoot(localCodeUserRoot) {
468
+ if (localCodeUserRoot.includes("Cursor"))
469
+ return "~/.config/Cursor/User";
470
+ if (localCodeUserRoot.includes("Code - Insiders"))
471
+ return "~/.config/Code - Insiders/User";
485
472
  return "~/.config/Code/User";
486
473
  }
487
-
488
- async function listDirectories(directory: string) {
474
+ async function listDirectories(directory) {
489
475
  try {
490
476
  const dirents = await import("node:fs/promises").then((fs) => fs.readdir(directory, { withFileTypes: true }));
491
477
  return dirents.filter((dirent) => dirent.isDirectory()).map((dirent) => path.join(directory, dirent.name));
492
- } catch {
478
+ }
479
+ catch {
493
480
  return [];
494
481
  }
495
482
  }
496
-
497
- async function listFiles(directory: string): Promise<string[]> {
498
- const result: string[] = [];
499
- async function visit(current: string) {
483
+ async function listFiles(directory) {
484
+ const result = [];
485
+ async function visit(current) {
500
486
  const dirents = await import("node:fs/promises").then((fs) => fs.readdir(current, { withFileTypes: true })).catch(() => []);
501
487
  for (const dirent of dirents) {
502
488
  const child = path.join(current, dirent.name);
503
- if (dirent.isDirectory()) await visit(child);
504
- else if (dirent.isFile()) result.push(child);
489
+ if (dirent.isDirectory())
490
+ await visit(child);
491
+ else if (dirent.isFile())
492
+ result.push(child);
505
493
  }
506
494
  }
507
495
  await visit(directory);
508
496
  return result;
509
497
  }
510
-
511
- function addCandidate(candidates: ContextCandidate[], source: string, remoteRoot: string, label: string, preferenceKey: string) {
498
+ function addCandidate(candidates, source, remoteRoot, label, preferenceKey) {
512
499
  candidates.push({ source, remoteRoot: expandRemoteHome(remoteRoot), label, sensitive: true, preferenceKey, promptLabel: label });
513
500
  }
514
-
515
- function dedupeCandidates(candidates: ContextCandidate[]) {
516
- const seen = new Set<string>();
501
+ function dedupeCandidates(candidates) {
502
+ const seen = new Set();
517
503
  return candidates.filter((candidate) => {
518
504
  const key = `${candidate.source}\0${candidate.remoteRoot}`;
519
- if (seen.has(key)) return false;
505
+ if (seen.has(key))
506
+ return false;
520
507
  seen.add(key);
521
508
  return true;
522
509
  });
523
510
  }
524
-
525
- function expandRemoteHome(value: string): string {
526
- if (value === "~") return "/root";
527
- if (value.startsWith("~/")) return `/root/${value.slice(2)}`;
511
+ function expandRemoteHome(value) {
512
+ if (value === "~")
513
+ return "/root";
514
+ if (value.startsWith("~/"))
515
+ return `/root/${value.slice(2)}`;
528
516
  return value;
529
517
  }
530
-
531
- function sqliteLiteral(value: string) {
518
+ function sqliteLiteral(value) {
532
519
  return `'${value.replace(/'/g, "''")}'`;
533
520
  }
534
-
535
- async function exists(filePath: string) {
521
+ async function exists(filePath) {
536
522
  try {
537
523
  await stat(filePath);
538
524
  return true;
539
- } catch {
525
+ }
526
+ catch {
540
527
  return false;
541
528
  }
542
529
  }
@@ -1,23 +1,29 @@
1
1
  import { stat } from "node:fs/promises";
2
2
  import { homedir } from "node:os";
3
3
  import path from "node:path";
4
- import { definePlugin, type ContextCandidate } from "../../../src/plugin-api.ts";
5
-
4
+ import { definePlugin } from "../../../src/plugin-api.js";
6
5
  export function awsAuthPlugin() {
7
6
  return definePlugin({
8
7
  name: "@freestyle-sync/auth-aws",
9
8
  async discoverContextCandidates({ options }) {
10
- if (!options.includeAuth) return [];
11
- const item: ContextCandidate = { source: path.join(homedir(), ".aws"), remoteRoot: "/root/.aws", label: "AWS credentials", sensitive: true, preferenceKey: "context:aws", promptLabel: "AWS credentials" };
9
+ if (!options.includeAuth)
10
+ return [];
11
+ const item = { source: path.join(homedir(), ".aws"), remoteRoot: "/root/.aws", label: "AWS credentials", sensitive: true, preferenceKey: "context:aws", promptLabel: "AWS credentials" };
12
12
  return await exists(item.source) ? [item] : [];
13
13
  },
14
14
  async afterContextSync({ vm, changedRemotePaths, utils }) {
15
- if (!changedRemotePaths.some((remotePath) => remotePath === "/root/.aws" || remotePath.startsWith("/root/.aws/"))) return;
15
+ if (!changedRemotePaths.some((remotePath) => remotePath === "/root/.aws" || remotePath.startsWith("/root/.aws/")))
16
+ return;
16
17
  await utils.checkedExec(vm, "chown -R root:root /root/.aws 2>/dev/null || true; chmod 700 /root/.aws 2>/dev/null || true");
17
18
  },
18
19
  });
19
20
  }
20
-
21
- async function exists(filePath: string) {
22
- try { await stat(filePath); return true; } catch { return false; }
21
+ async function exists(filePath) {
22
+ try {
23
+ await stat(filePath);
24
+ return true;
25
+ }
26
+ catch {
27
+ return false;
28
+ }
23
29
  }