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