@sand.ai/vibable-cli 0.1.0 → 0.3.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -1 +1 @@
1
- {"version":3,"file":"protocol.d.ts","sourceRoot":"","sources":["../src/protocol.ts"],"names":[],"mappings":"AAAA,eAAO,MAAM,wBAAwB,kBAAkB,CAAC;AACxD,eAAO,MAAM,qBAAqB,QAAkB,CAAC;AACrD,eAAO,MAAM,0BAA0B,MAAM,CAAC;AAE9C,MAAM,MAAM,sBAAsB,GAC9B,QAAQ,GACR,cAAc,GACd,SAAS,GACT,SAAS,CAAC;AAEd,MAAM,MAAM,uBAAuB,GAAG;IACpC,EAAE,EAAE,MAAM,CAAC;IACX,MAAM,EAAE,sBAAsB,CAAC;IAC/B,eAAe,EAAE,MAAM,CAAC;IACxB,aAAa,EAAE,MAAM,GAAG,IAAI,CAAC;IAC7B,YAAY,EAAE,MAAM,CAAC;IACrB,UAAU,EAAE,MAAM,GAAG,IAAI,CAAC;IAC1B,UAAU,EAAE,MAAM,GAAG,IAAI,CAAC;IAC1B,eAAe,EAAE,MAAM,EAAE,CAAC;IAC1B,SAAS,EAAE,MAAM,CAAC;IAClB,SAAS,EAAE,MAAM,CAAC;CACnB,CAAC;AAEF,MAAM,MAAM,kBAAkB,GAC1B;IACE,IAAI,EAAE,QAAQ,CAAC;IACf,IAAI,EAAE,MAAM,CAAC;IACb,aAAa,EAAE,MAAM,CAAC;IACtB,IAAI,EAAE,MAAM,CAAC;IACb,UAAU,CAAC,EAAE,OAAO,CAAC;CACtB,GACD;IAAE,IAAI,EAAE,QAAQ,CAAC;IAAC,IAAI,EAAE,MAAM,CAAA;CAAE,GAChC;IAAE,IAAI,EAAE,OAAO,CAAC;IAAC,IAAI,EAAE,MAAM,CAAA;CAAE,CAAC;AAEpC,MAAM,MAAM,sBAAsB,GAAG;IACnC,IAAI,EAAE,MAAM,CAAC;IACb,IAAI,EAAE,MAAM,GAAG,WAAW,CAAC;IAC3B,IAAI,EAAE,MAAM,CAAC;IACb,IAAI,EAAE,MAAM,GAAG,IAAI,CAAC;IACpB,SAAS,EAAE,MAAM,CAAC;CACnB,CAAC;AAcF,wBAAgB,yBAAyB,CAAC,IAAI,EAAE,MAAM,WAUrD;AAED,wBAAgB,mBAAmB,CAAC,IAAI,EAAE,MAAM,UAa/C"}
1
+ {"version":3,"file":"protocol.d.ts","sourceRoot":"","sources":["../src/protocol.ts"],"names":[],"mappings":"AAAA,eAAO,MAAM,wBAAwB,kBAAkB,CAAC;AACxD,eAAO,MAAM,qBAAqB,QAAkB,CAAC;AACrD,eAAO,MAAM,0BAA0B,MAAM,CAAC;AAE9C,MAAM,MAAM,sBAAsB,GAC9B,QAAQ,GACR,cAAc,GACd,SAAS,GACT,SAAS,CAAC;AAEd,MAAM,MAAM,uBAAuB,GAAG;IACpC,EAAE,EAAE,MAAM,CAAC;IACX,MAAM,EAAE,sBAAsB,CAAC;IAC/B,eAAe,EAAE,MAAM,CAAC;IACxB,aAAa,EAAE,MAAM,GAAG,IAAI,CAAC;IAC7B,YAAY,EAAE,MAAM,CAAC;IACrB,UAAU,EAAE,MAAM,GAAG,IAAI,CAAC;IAC1B,UAAU,EAAE,MAAM,GAAG,IAAI,CAAC;IAC1B,eAAe,EAAE,MAAM,EAAE,CAAC;IAC1B,SAAS,EAAE,MAAM,CAAC;IAClB,SAAS,EAAE,MAAM,CAAC;CACnB,CAAC;AAEF,MAAM,MAAM,qBAAqB,GAAG;IAClC,EAAE,EAAE,MAAM,CAAC;IACX,WAAW,EAAE,MAAM,CAAC;CACrB,CAAC;AAEF,MAAM,MAAM,0BAA0B,GAAG;IACvC,OAAO,EAAE,uBAAuB,CAAC;IACjC,YAAY,EAAE,qBAAqB,GAAG,IAAI,CAAC;CAC5C,CAAC;AAEF,MAAM,MAAM,kBAAkB,GAC1B;IACE,IAAI,EAAE,QAAQ,CAAC;IACf,IAAI,EAAE,MAAM,CAAC;IACb,aAAa,EAAE,MAAM,CAAC;IACtB,IAAI,EAAE,MAAM,CAAC;IACb,UAAU,CAAC,EAAE,OAAO,CAAC;CACtB,GACD;IAAE,IAAI,EAAE,QAAQ,CAAC;IAAC,IAAI,EAAE,MAAM,CAAA;CAAE,GAChC;IAAE,IAAI,EAAE,OAAO,CAAC;IAAC,IAAI,EAAE,MAAM,CAAA;CAAE,CAAC;AAEpC,MAAM,MAAM,sBAAsB,GAAG;IACnC,IAAI,EAAE,MAAM,CAAC;IACb,IAAI,EAAE,MAAM,GAAG,WAAW,CAAC;IAC3B,IAAI,EAAE,MAAM,CAAC;IACb,IAAI,EAAE,MAAM,GAAG,IAAI,CAAC;IACpB,SAAS,EAAE,MAAM,CAAC;CACnB,CAAC;AAcF,wBAAgB,yBAAyB,CAAC,IAAI,EAAE,MAAM,WAUrD;AAED,wBAAgB,mBAAmB,CAAC,IAAI,EAAE,MAAM,UAa/C"}
@@ -1 +1 @@
1
- {"version":3,"file":"protocol.js","sourceRoot":"","sources":["../src/protocol.ts"],"names":[],"mappings":"AAAA,MAAM,CAAC,MAAM,wBAAwB,GAAG,eAAe,CAAC;AACxD,MAAM,CAAC,MAAM,qBAAqB,GAAG,CAAC,GAAG,IAAI,GAAG,IAAI,CAAC;AACrD,MAAM,CAAC,MAAM,0BAA0B,GAAG,GAAG,CAAC;AAwC9C,MAAM,qBAAqB,GAAG,IAAI,GAAG,CAAC;IACpC,MAAM;IACN,UAAU;IACV,cAAc;IACd,OAAO;IACP,QAAQ;IACR,QAAQ;IACR,MAAM;IACN,OAAO;IACP,UAAU;CACX,CAAC,CAAC;AAEH,MAAM,UAAU,yBAAyB,CAAC,IAAY;IACpD,OAAO,IAAI;SACR,KAAK,CAAC,OAAO,CAAC;SACd,MAAM,CAAC,OAAO,CAAC;SACf,IAAI,CACH,CAAC,OAAO,EAAE,EAAE,CACV,qBAAqB,CAAC,GAAG,CAAC,OAAO,CAAC;QAClC,OAAO,KAAK,WAAW;QACvB,OAAO,CAAC,UAAU,CAAC,MAAM,CAAC,CAC7B,CAAC;AACN,CAAC;AAED,MAAM,UAAU,mBAAmB,CAAC,IAAY;IAC9C,IACE,IAAI,CAAC,MAAM,KAAK,CAAC;QACjB,IAAI,CAAC,MAAM,GAAG,IAAI;QAClB,IAAI,CAAC,QAAQ,CAAC,IAAI,CAAC;QACnB,IAAI,CAAC,UAAU,CAAC,GAAG,CAAC;QACpB,IAAI,CAAC,KAAK,CAAC,OAAO,CAAC,CAAC,QAAQ,CAAC,IAAI,CAAC;QAClC,yBAAyB,CAAC,IAAI,CAAC,EAC/B,CAAC;QACD,MAAM,IAAI,KAAK,CAAC,4CAA4C,IAAI,EAAE,CAAC,CAAC;IACtE,CAAC;IAED,OAAO,IAAI,CAAC;AACd,CAAC"}
1
+ {"version":3,"file":"protocol.js","sourceRoot":"","sources":["../src/protocol.ts"],"names":[],"mappings":"AAAA,MAAM,CAAC,MAAM,wBAAwB,GAAG,eAAe,CAAC;AACxD,MAAM,CAAC,MAAM,qBAAqB,GAAG,CAAC,GAAG,IAAI,GAAG,IAAI,CAAC;AACrD,MAAM,CAAC,MAAM,0BAA0B,GAAG,GAAG,CAAC;AAkD9C,MAAM,qBAAqB,GAAG,IAAI,GAAG,CAAC;IACpC,MAAM;IACN,UAAU;IACV,cAAc;IACd,OAAO;IACP,QAAQ;IACR,QAAQ;IACR,MAAM;IACN,OAAO;IACP,UAAU;CACX,CAAC,CAAC;AAEH,MAAM,UAAU,yBAAyB,CAAC,IAAY;IACpD,OAAO,IAAI;SACR,KAAK,CAAC,OAAO,CAAC;SACd,MAAM,CAAC,OAAO,CAAC;SACf,IAAI,CACH,CAAC,OAAO,EAAE,EAAE,CACV,qBAAqB,CAAC,GAAG,CAAC,OAAO,CAAC;QAClC,OAAO,KAAK,WAAW;QACvB,OAAO,CAAC,UAAU,CAAC,MAAM,CAAC,CAC7B,CAAC;AACN,CAAC;AAED,MAAM,UAAU,mBAAmB,CAAC,IAAY;IAC9C,IACE,IAAI,CAAC,MAAM,KAAK,CAAC;QACjB,IAAI,CAAC,MAAM,GAAG,IAAI;QAClB,IAAI,CAAC,QAAQ,CAAC,IAAI,CAAC;QACnB,IAAI,CAAC,UAAU,CAAC,GAAG,CAAC;QACpB,IAAI,CAAC,KAAK,CAAC,OAAO,CAAC,CAAC,QAAQ,CAAC,IAAI,CAAC;QAClC,yBAAyB,CAAC,IAAI,CAAC,EAC/B,CAAC;QACD,MAAM,IAAI,KAAK,CAAC,4CAA4C,IAAI,EAAE,CAAC,CAAC;IACtE,CAAC;IAED,OAAO,IAAI,CAAC;AACd,CAAC"}
@@ -0,0 +1,9 @@
1
+ export type ServerSentEvent = {
2
+ event: string;
3
+ data: string;
4
+ };
5
+ export declare function extractServerSentEvents(source: string): {
6
+ events: ServerSentEvent[];
7
+ remainder: string;
8
+ };
9
+ //# sourceMappingURL=server-sent-events.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"server-sent-events.d.ts","sourceRoot":"","sources":["../src/server-sent-events.ts"],"names":[],"mappings":"AAAA,MAAM,MAAM,eAAe,GAAG;IAC5B,KAAK,EAAE,MAAM,CAAC;IACd,IAAI,EAAE,MAAM,CAAC;CACd,CAAC;AAEF,wBAAgB,uBAAuB,CAAC,MAAM,EAAE,MAAM,GAAG;IACvD,MAAM,EAAE,eAAe,EAAE,CAAC;IAC1B,SAAS,EAAE,MAAM,CAAC;CACnB,CAOA"}
@@ -0,0 +1,25 @@
1
+ export function extractServerSentEvents(source) {
2
+ const normalized = source.replace(/\r\n/gu, "\n");
3
+ const blocks = normalized.split("\n\n");
4
+ const remainder = blocks.pop() ?? "";
5
+ const events = blocks.flatMap(parseEventBlock);
6
+ return { events, remainder };
7
+ }
8
+ function parseEventBlock(block) {
9
+ let event = "message";
10
+ const data = [];
11
+ for (const line of block.split("\n")) {
12
+ if (line.startsWith(":"))
13
+ continue;
14
+ const separator = line.indexOf(":");
15
+ const field = separator < 0 ? line : line.slice(0, separator);
16
+ const rawValue = separator < 0 ? "" : line.slice(separator + 1);
17
+ const value = rawValue.startsWith(" ") ? rawValue.slice(1) : rawValue;
18
+ if (field === "event")
19
+ event = value;
20
+ if (field === "data")
21
+ data.push(value);
22
+ }
23
+ return data.length > 0 ? [{ event, data: data.join("\n") }] : [];
24
+ }
25
+ //# sourceMappingURL=server-sent-events.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"server-sent-events.js","sourceRoot":"","sources":["../src/server-sent-events.ts"],"names":[],"mappings":"AAKA,MAAM,UAAU,uBAAuB,CAAC,MAAc;IAIpD,MAAM,UAAU,GAAG,MAAM,CAAC,OAAO,CAAC,QAAQ,EAAE,IAAI,CAAC,CAAC;IAClD,MAAM,MAAM,GAAG,UAAU,CAAC,KAAK,CAAC,MAAM,CAAC,CAAC;IACxC,MAAM,SAAS,GAAG,MAAM,CAAC,GAAG,EAAE,IAAI,EAAE,CAAC;IACrC,MAAM,MAAM,GAAG,MAAM,CAAC,OAAO,CAAC,eAAe,CAAC,CAAC;IAE/C,OAAO,EAAE,MAAM,EAAE,SAAS,EAAE,CAAC;AAC/B,CAAC;AAED,SAAS,eAAe,CAAC,KAAa;IACpC,IAAI,KAAK,GAAG,SAAS,CAAC;IACtB,MAAM,IAAI,GAAa,EAAE,CAAC;IAE1B,KAAK,MAAM,IAAI,IAAI,KAAK,CAAC,KAAK,CAAC,IAAI,CAAC,EAAE,CAAC;QACrC,IAAI,IAAI,CAAC,UAAU,CAAC,GAAG,CAAC;YAAE,SAAS;QACnC,MAAM,SAAS,GAAG,IAAI,CAAC,OAAO,CAAC,GAAG,CAAC,CAAC;QACpC,MAAM,KAAK,GAAG,SAAS,GAAG,CAAC,CAAC,CAAC,CAAC,IAAI,CAAC,CAAC,CAAC,IAAI,CAAC,KAAK,CAAC,CAAC,EAAE,SAAS,CAAC,CAAC;QAC9D,MAAM,QAAQ,GAAG,SAAS,GAAG,CAAC,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,IAAI,CAAC,KAAK,CAAC,SAAS,GAAG,CAAC,CAAC,CAAC;QAChE,MAAM,KAAK,GAAG,QAAQ,CAAC,UAAU,CAAC,GAAG,CAAC,CAAC,CAAC,CAAC,QAAQ,CAAC,KAAK,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,QAAQ,CAAC;QACtE,IAAI,KAAK,KAAK,OAAO;YAAE,KAAK,GAAG,KAAK,CAAC;QACrC,IAAI,KAAK,KAAK,MAAM;YAAE,IAAI,CAAC,IAAI,CAAC,KAAK,CAAC,CAAC;IACzC,CAAC;IAED,OAAO,IAAI,CAAC,MAAM,GAAG,CAAC,CAAC,CAAC,CAAC,CAAC,EAAE,KAAK,EAAE,IAAI,EAAE,IAAI,CAAC,IAAI,CAAC,IAAI,CAAC,EAAE,CAAC,CAAC,CAAC,CAAC,EAAE,CAAC;AACnE,CAAC"}
package/dist/sync.d.ts CHANGED
@@ -14,11 +14,18 @@ export type LocalEntry = {
14
14
  hash: string;
15
15
  executable: boolean;
16
16
  };
17
+ type SyncDaemonState = {
18
+ pid: number;
19
+ status: "starting" | "running";
20
+ startedAt: string;
21
+ logFile: string;
22
+ };
17
23
  export declare function connectAndWatch(input: {
18
24
  apiBaseUrl: string;
19
25
  sessionId: string;
20
26
  token: string;
21
27
  directory: string;
28
+ onConnected?: () => void | Promise<void>;
22
29
  }): Promise<void>;
23
30
  export declare function runRemoteCommand(input: {
24
31
  apiBaseUrl: string;
@@ -28,12 +35,23 @@ export declare function runRemoteCommand(input: {
28
35
  command: string[];
29
36
  }): Promise<void>;
30
37
  export declare function readLocalConfig(cwd?: string): Promise<LocalConfig>;
38
+ export declare function startSyncDaemon(cwd?: string): Promise<SyncDaemonState>;
39
+ export declare function isSyncDaemonRunning(cwd?: string): Promise<boolean>;
40
+ export declare function runSyncDaemon(root: string): Promise<void>;
41
+ export declare function showSyncDaemonStatus(cwd?: string): Promise<void>;
42
+ export declare function stopSyncDaemon(cwd?: string): Promise<void>;
31
43
  export declare function manifestEntriesToLocalEntries(entries: RemoteDevManifestEntry[]): Map<string, LocalEntry>;
32
44
  export declare function buildSyncEvents(previous: Map<string, LocalEntry>, next: Map<string, LocalEntry>): RemoteDevSyncEvent[];
33
45
  export declare function findRemoteDevConflicts(previous: Map<string, LocalEntry> | null, remote: Map<string, LocalEntry>, local: Map<string, LocalEntry>): string[];
34
46
  export declare function scanLocalTree(root: string, options?: {
35
47
  onSkippedLargeFile?: (relativePath: string, size: number) => void;
36
48
  }): Promise<Map<string, LocalEntry>>;
49
+ export declare function enableRemoteDevAppAuth(config: LocalConfig): Promise<void>;
50
+ export declare function runRemoteDataMigration(input: {
51
+ config: LocalConfig;
52
+ inputPath: string;
53
+ wait: boolean;
54
+ }): Promise<void>;
37
55
  export declare class HttpError extends Error {
38
56
  readonly status: number;
39
57
  constructor(message: string, status: number);
@@ -53,4 +71,5 @@ export declare function hydrateUpsertContents(root: string, events: RemoteDevSyn
53
71
  hash: string;
54
72
  executable?: boolean;
55
73
  })[]>;
74
+ export {};
56
75
  //# sourceMappingURL=sync.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"sync.d.ts","sourceRoot":"","sources":["../src/sync.ts"],"names":[],"mappings":"AAIA,OAAO,EAML,KAAK,sBAAsB,EAE3B,KAAK,kBAAkB,EACxB,MAAM,eAAe,CAAC;AAIvB,eAAO,MAAM,UAAU,UAAU,CAAC;AAGlC,MAAM,MAAM,WAAW,GAAG;IACxB,UAAU,EAAE,MAAM,CAAC;IACnB,SAAS,EAAE,MAAM,CAAC;IAClB,KAAK,EAAE,MAAM,CAAC;CACf,CAAC;AAEF,MAAM,MAAM,UAAU,GAClB;IAAE,IAAI,EAAE,WAAW,CAAC;IAAC,IAAI,EAAE,MAAM,CAAA;CAAE,GACnC;IAAE,IAAI,EAAE,MAAM,CAAC;IAAC,IAAI,EAAE,MAAM,CAAC;IAAC,IAAI,EAAE,MAAM,CAAC;IAAC,UAAU,EAAE,OAAO,CAAA;CAAE,CAAC;AAgBtE,wBAAsB,eAAe,CAAC,KAAK,EAAE;IAC3C,UAAU,EAAE,MAAM,CAAC;IACnB,SAAS,EAAE,MAAM,CAAC;IAClB,KAAK,EAAE,MAAM,CAAC;IACd,SAAS,EAAE,MAAM,CAAC;CACnB,iBA8FA;AAqBD,wBAAsB,gBAAgB,CAAC,KAAK,EAAE;IAC5C,UAAU,EAAE,MAAM,CAAC;IACnB,SAAS,EAAE,MAAM,CAAC;IAClB,KAAK,EAAE,MAAM,CAAC;IACd,KAAK,EAAE,OAAO,CAAC;IACf,OAAO,EAAE,MAAM,EAAE,CAAC;CACnB,iBAgCA;AAED,wBAAsB,eAAe,CAAC,GAAG,SAAgB,GAAG,OAAO,CAAC,WAAW,CAAC,CAmB/E;AAED,wBAAgB,6BAA6B,CAAC,OAAO,EAAE,sBAAsB,EAAE,2BAmB9E;AAwED,wBAAgB,eAAe,CAC7B,QAAQ,EAAE,GAAG,CAAC,MAAM,EAAE,UAAU,CAAC,EACjC,IAAI,EAAE,GAAG,CAAC,MAAM,EAAE,UAAU,CAAC,wBAyC9B;AAED,wBAAgB,sBAAsB,CACpC,QAAQ,EAAE,GAAG,CAAC,MAAM,EAAE,UAAU,CAAC,GAAG,IAAI,EACxC,MAAM,EAAE,GAAG,CAAC,MAAM,EAAE,UAAU,CAAC,EAC/B,KAAK,EAAE,GAAG,CAAC,MAAM,EAAE,UAAU,CAAC,YAmB/B;AAED,wBAAsB,aAAa,CACjC,IAAI,EAAE,MAAM,EACZ,OAAO,GAAE;IAAE,kBAAkB,CAAC,EAAE,CAAC,YAAY,EAAE,MAAM,EAAE,IAAI,EAAE,MAAM,KAAK,IAAI,CAAA;CAAO,oCAKpF;AA+RD,qBAAa,SAAU,SAAQ,KAAK;IAGhC,QAAQ,CAAC,MAAM,EAAE,MAAM;gBADvB,OAAO,EAAE,MAAM,EACN,MAAM,EAAE,MAAM;CAK1B;AAED,wBAAgB,wBAAwB,CAAC,KAAK,EAAE,OAAO,WAEtD;AAED,wBAAgB,WAAW,CAAC,MAAM,EAAE,kBAAkB,EAAE,EAAE,IAAI,EAAE,MAAM,0BAMrE;AAWD,wBAAsB,qBAAqB,CACzC,IAAI,EAAE,MAAM,EACZ,MAAM,EAAE,kBAAkB,EAAE;;;;;;;;;;;;MAa7B"}
1
+ {"version":3,"file":"sync.d.ts","sourceRoot":"","sources":["../src/sync.ts"],"names":[],"mappings":"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"}
package/dist/sync.js CHANGED
@@ -1,10 +1,12 @@
1
+ import { spawn } from "node:child_process";
1
2
  import { createHash } from "node:crypto";
2
- import { lstat, mkdir, readdir, readFile, writeFile } from "node:fs/promises";
3
+ import { lstat, mkdir, open, readdir, readFile, unlink, writeFile } from "node:fs/promises";
3
4
  import path from "node:path";
4
5
  import { assertRemoteDevPath, remoteDevMaxEventsPerBatch, remoteDevMaxFileBytes, remoteDevProtocolVersion, shouldIgnoreRemoteDevPath, } from "./protocol.js";
5
6
  import { extractStoredZip } from "./zip.js";
6
7
  export const cliVersion = "0.1.0";
7
- const heartbeatIntervalMs = 15_000;
8
+ const heartbeatIntervalMs = 2_000;
9
+ const checkpointQuietPeriodMs = 3_000;
8
10
  export async function connectAndWatch(input) {
9
11
  const root = path.resolve(input.directory);
10
12
  await mkdir(root, { recursive: true });
@@ -18,6 +20,7 @@ export async function connectAndWatch(input) {
18
20
  localPath: root,
19
21
  },
20
22
  });
23
+ const agentInstructions = requireAgentInstructions(connected);
21
24
  const skippedLargeFiles = new Set();
22
25
  const onSkippedLargeFile = (relativePath, size) => {
23
26
  const key = `${relativePath}:${size}`;
@@ -34,6 +37,7 @@ export async function connectAndWatch(input) {
34
37
  });
35
38
  await extractStoredZip({ archive, destination: root });
36
39
  }
40
+ await writeAgentInstructions(root, agentInstructions);
37
41
  const manifest = await getJson({
38
42
  ...input,
39
43
  path: `/api/remote-dev/sessions/${input.sessionId}/manifest`,
@@ -53,30 +57,90 @@ export async function connectAndWatch(input) {
53
57
  let previous = remoteBaseline;
54
58
  let sequence = connected.session.lastSequence + 1;
55
59
  let lastHeartbeatAt = 0;
56
- process.stdout.write(`Connected remote dev session ${input.sessionId}\n`);
57
- await new Promise((_resolve, reject) => {
60
+ let lastChangeAt = 0;
61
+ let lastCheckpointSequence = connected.session.lastSequence;
62
+ process.stdout.write(`Connected local development session ${input.sessionId}\n`);
63
+ await input.onConnected?.();
64
+ await new Promise((resolve, reject) => {
58
65
  let running = false;
66
+ let stopping = false;
67
+ let activeTick = Promise.resolve();
68
+ const cleanupSignals = () => {
69
+ process.removeListener("SIGINT", handleSigint);
70
+ process.removeListener("SIGTERM", handleSigterm);
71
+ };
72
+ const stop = () => {
73
+ if (stopping)
74
+ return;
75
+ stopping = true;
76
+ clearInterval(interval);
77
+ activeTick
78
+ .then(async () => {
79
+ let stopError = null;
80
+ try {
81
+ const finalSync = await syncOnce({
82
+ ...input,
83
+ root,
84
+ sequence,
85
+ previous,
86
+ onSkippedLargeFile,
87
+ });
88
+ previous = finalSync.nextEntries;
89
+ sequence = finalSync.nextSequence;
90
+ if (getAppliedSequence(sequence) > lastCheckpointSequence) {
91
+ await checkpointRemoteDev(input);
92
+ }
93
+ }
94
+ catch (error) {
95
+ stopError = error;
96
+ }
97
+ try {
98
+ await exitRemoteDev(input);
99
+ }
100
+ catch (error) {
101
+ stopError ??= error;
102
+ }
103
+ cleanupSignals();
104
+ if (stopError)
105
+ throw stopError;
106
+ process.stdout.write("Saved and disconnected local development session.\n");
107
+ resolve();
108
+ })
109
+ .catch((error) => {
110
+ cleanupSignals();
111
+ reject(error);
112
+ });
113
+ };
114
+ const handleSigint = () => stop();
115
+ const handleSigterm = () => stop();
116
+ process.once("SIGINT", handleSigint);
117
+ process.once("SIGTERM", handleSigterm);
59
118
  const interval = setInterval(() => {
60
- if (running) {
119
+ if (running || stopping) {
61
120
  return;
62
121
  }
63
122
  running = true;
64
- void runConnectLoopTick({
123
+ activeTick = runConnectLoopTick({
65
124
  ...input,
66
125
  root,
67
126
  sequence,
68
127
  previous,
69
128
  lastHeartbeatAt,
129
+ lastChangeAt,
130
+ lastCheckpointSequence,
70
131
  onSkippedLargeFile,
71
132
  })
72
133
  .then((result) => {
73
134
  previous = result.nextEntries;
74
135
  sequence = result.nextSequence;
75
136
  lastHeartbeatAt = result.nextHeartbeatAt;
137
+ lastChangeAt = result.nextChangeAt;
138
+ lastCheckpointSequence = result.nextCheckpointSequence;
76
139
  })
77
140
  .catch((error) => {
78
141
  if (isTerminalRemoteDevError(error)) {
79
142
  clearInterval(interval);
143
+ cleanupSignals();
80
144
  reject(error);
81
145
  return;
82
146
  }
@@ -88,13 +152,56 @@ export async function connectAndWatch(input) {
88
152
  }, 500);
89
153
  });
90
154
  }
155
+ function requireAgentInstructions(response) {
156
+ const instructions = response.agentInstructions?.trim();
157
+ if (!instructions) {
158
+ throw new Error("Remote dev connect response did not include agent instructions. Update the Vibable API before connecting.");
159
+ }
160
+ return instructions;
161
+ }
91
162
  async function runConnectLoopTick(input) {
92
163
  const now = Date.now();
164
+ const result = await syncOnce(input);
165
+ let nextResult = result;
93
166
  const nextHeartbeatAt = now - input.lastHeartbeatAt >= heartbeatIntervalMs
94
- ? await sendHeartbeat(input).then(() => now)
167
+ ? await sendHeartbeat({
168
+ ...input,
169
+ clean: true,
170
+ lastSequence: getAppliedSequence(result.nextSequence),
171
+ }).then(async (heartbeat) => {
172
+ if (heartbeat.flushRequest) {
173
+ nextResult = await syncOnce({
174
+ ...input,
175
+ sequence: result.nextSequence,
176
+ previous: result.nextEntries,
177
+ });
178
+ await sendFlushAck({
179
+ ...input,
180
+ requestId: heartbeat.flushRequest.id,
181
+ lastSequence: getAppliedSequence(nextResult.nextSequence),
182
+ });
183
+ }
184
+ return now;
185
+ })
95
186
  : input.lastHeartbeatAt;
96
- const result = await syncOnce(input);
97
- return { ...result, nextHeartbeatAt };
187
+ const changedThisTick = result.changed || nextResult.changed;
188
+ const nextChangeAt = changedThisTick ? now : input.lastChangeAt;
189
+ const appliedSequence = getAppliedSequence(nextResult.nextSequence);
190
+ const shouldCheckpoint = !changedThisTick &&
191
+ appliedSequence > input.lastCheckpointSequence &&
192
+ now - nextChangeAt >= checkpointQuietPeriodMs;
193
+ if (shouldCheckpoint) {
194
+ await checkpointRemoteDev(input);
195
+ }
196
+ return {
197
+ ...nextResult,
198
+ changed: changedThisTick,
199
+ nextHeartbeatAt,
200
+ nextChangeAt,
201
+ nextCheckpointSequence: shouldCheckpoint
202
+ ? appliedSequence
203
+ : input.lastCheckpointSequence,
204
+ };
98
205
  }
99
206
  export async function runRemoteCommand(input) {
100
207
  const command = input.command[0];
@@ -122,7 +229,110 @@ export async function runRemoteCommand(input) {
122
229
  process.exitCode = exitCode;
123
230
  }
124
231
  export async function readLocalConfig(cwd = process.cwd()) {
125
- const parsed = JSON.parse(await readNearestLocalConfig(path.resolve(cwd)));
232
+ return (await readLocalProject(cwd)).config;
233
+ }
234
+ export async function startSyncDaemon(cwd = process.cwd()) {
235
+ const project = await readLocalProject(cwd);
236
+ const existing = await readSyncDaemonState(project.root);
237
+ if (existing && isProcessRunning(existing.pid)) {
238
+ process.stdout.write(`Vibable sync is already running (pid ${existing.pid}).\n`);
239
+ return existing;
240
+ }
241
+ const logFile = path.join(project.root, ".vibable", "sync.log");
242
+ const output = await open(logFile, "a", 0o600);
243
+ try {
244
+ const entryFile = process.argv[1];
245
+ if (!entryFile)
246
+ throw new Error("Cannot locate the Vibable CLI entry file.");
247
+ const child = spawn(process.execPath, [entryFile, "sync", "daemon", project.root], {
248
+ cwd: project.root,
249
+ detached: true,
250
+ stdio: ["ignore", output.fd, output.fd],
251
+ });
252
+ if (!child.pid)
253
+ throw new Error("Failed to start Vibable sync.");
254
+ const state = {
255
+ pid: child.pid,
256
+ status: "starting",
257
+ startedAt: new Date().toISOString(),
258
+ logFile,
259
+ };
260
+ await writeSyncDaemonState(project.root, state);
261
+ child.unref();
262
+ process.stdout.write(`Vibable sync started in the background (pid ${child.pid}).\n`);
263
+ return state;
264
+ }
265
+ finally {
266
+ await output.close();
267
+ }
268
+ }
269
+ export async function isSyncDaemonRunning(cwd = process.cwd()) {
270
+ const { root } = await readLocalProject(cwd);
271
+ const state = await readSyncDaemonState(root);
272
+ return Boolean(state && isProcessRunning(state.pid));
273
+ }
274
+ export async function runSyncDaemon(root) {
275
+ const project = await readLocalProject(root);
276
+ const state = await waitForOwnSyncDaemonState(project.root);
277
+ if (!state || state.pid !== process.pid) {
278
+ throw new Error("Vibable sync daemon state does not match this process.");
279
+ }
280
+ try {
281
+ await connectAndWatch({
282
+ ...project.config,
283
+ directory: project.root,
284
+ onConnected: async () => {
285
+ await writeSyncDaemonState(project.root, { ...state, status: "running" });
286
+ },
287
+ });
288
+ }
289
+ finally {
290
+ const current = await readSyncDaemonState(project.root);
291
+ if (current?.pid === process.pid) {
292
+ await unlink(syncDaemonStatePath(project.root)).catch((error) => {
293
+ if (!isMissingFileError(error))
294
+ throw error;
295
+ });
296
+ }
297
+ }
298
+ }
299
+ async function waitForOwnSyncDaemonState(root) {
300
+ const deadline = Date.now() + 2_000;
301
+ while (Date.now() < deadline) {
302
+ const state = await readSyncDaemonState(root);
303
+ if (state?.pid === process.pid)
304
+ return state;
305
+ await new Promise((resolve) => setTimeout(resolve, 25));
306
+ }
307
+ return null;
308
+ }
309
+ export async function showSyncDaemonStatus(cwd = process.cwd()) {
310
+ const { root } = await readLocalProject(cwd);
311
+ const state = await readSyncDaemonState(root);
312
+ const running = Boolean(state && isProcessRunning(state.pid));
313
+ process.stdout.write(`${JSON.stringify({ running, ...(state ?? {}) }, null, 2)}\n`);
314
+ }
315
+ export async function stopSyncDaemon(cwd = process.cwd()) {
316
+ const { root } = await readLocalProject(cwd);
317
+ const state = await readSyncDaemonState(root);
318
+ if (!state || !isProcessRunning(state.pid)) {
319
+ await unlink(syncDaemonStatePath(root)).catch(() => undefined);
320
+ process.stdout.write("Vibable sync is not running.\n");
321
+ return;
322
+ }
323
+ process.kill(state.pid, "SIGTERM");
324
+ const deadline = Date.now() + 30_000;
325
+ while (Date.now() < deadline && isProcessRunning(state.pid)) {
326
+ await new Promise((resolve) => setTimeout(resolve, 250));
327
+ }
328
+ if (isProcessRunning(state.pid)) {
329
+ throw new Error("Vibable sync is still saving. Check .vibable/sync.log and retry.");
330
+ }
331
+ process.stdout.write("Vibable sync saved and stopped.\n");
332
+ }
333
+ async function readLocalProject(cwd) {
334
+ const located = await readNearestLocalConfig(path.resolve(cwd));
335
+ const parsed = JSON.parse(located.source);
126
336
  if (!parsed ||
127
337
  typeof parsed !== "object" ||
128
338
  !("apiBaseUrl" in parsed) ||
@@ -134,9 +344,12 @@ export async function readLocalConfig(cwd = process.cwd()) {
134
344
  throw new Error("Invalid .vibable/config.json");
135
345
  }
136
346
  return {
137
- apiBaseUrl: parsed.apiBaseUrl,
138
- sessionId: parsed.sessionId,
139
- token: parsed.token,
347
+ root: located.root,
348
+ config: {
349
+ apiBaseUrl: parsed.apiBaseUrl,
350
+ sessionId: parsed.sessionId,
351
+ token: parsed.token,
352
+ },
140
353
  };
141
354
  }
142
355
  export function manifestEntriesToLocalEntries(entries) {
@@ -163,7 +376,7 @@ async function readNearestLocalConfig(startDirectory) {
163
376
  while (true) {
164
377
  const configPath = path.join(currentDirectory, ".vibable", "config.json");
165
378
  try {
166
- return await readFile(configPath, "utf8");
379
+ return { root: currentDirectory, source: await readFile(configPath, "utf8") };
167
380
  }
168
381
  catch (error) {
169
382
  if (!isMissingFileError(error)) {
@@ -177,6 +390,38 @@ async function readNearestLocalConfig(startDirectory) {
177
390
  currentDirectory = parentDirectory;
178
391
  }
179
392
  }
393
+ async function readSyncDaemonState(root) {
394
+ try {
395
+ const value = JSON.parse(await readFile(syncDaemonStatePath(root), "utf8"));
396
+ if (typeof value.pid === "number" &&
397
+ (value.status === "starting" || value.status === "running") &&
398
+ typeof value.startedAt === "string" &&
399
+ typeof value.logFile === "string") {
400
+ return value;
401
+ }
402
+ return null;
403
+ }
404
+ catch (error) {
405
+ if (isMissingFileError(error))
406
+ return null;
407
+ throw error;
408
+ }
409
+ }
410
+ async function writeSyncDaemonState(root, state) {
411
+ await writeFile(syncDaemonStatePath(root), `${JSON.stringify(state, null, 2)}\n`, { mode: 0o600 });
412
+ }
413
+ function syncDaemonStatePath(root) {
414
+ return path.join(root, ".vibable", "sync-daemon.json");
415
+ }
416
+ function isProcessRunning(pid) {
417
+ try {
418
+ process.kill(pid, 0);
419
+ return true;
420
+ }
421
+ catch {
422
+ return false;
423
+ }
424
+ }
180
425
  function isMissingFileError(error) {
181
426
  return Boolean(error &&
182
427
  typeof error === "object" &&
@@ -189,7 +434,7 @@ async function syncOnce(input) {
189
434
  : {});
190
435
  const events = buildSyncEvents(input.previous, nextEntries);
191
436
  if (events.length === 0) {
192
- return { nextEntries, nextSequence: input.sequence };
437
+ return { nextEntries, nextSequence: input.sequence, changed: false };
193
438
  }
194
439
  let nextSequence = input.sequence;
195
440
  for (const chunk of chunkEvents(events, remoteDevMaxEventsPerBatch)) {
@@ -208,7 +453,7 @@ async function syncOnce(input) {
208
453
  }
209
454
  await writeLocalState(input.root, { baseline: localEntriesToStateEntries(nextEntries) });
210
455
  process.stdout.write(`synced ${events.length} change(s)\n`);
211
- return { nextEntries, nextSequence };
456
+ return { nextEntries, nextSequence, changed: true };
212
457
  }
213
458
  export function buildSyncEvents(previous, next) {
214
459
  const events = [];
@@ -332,14 +577,49 @@ async function getJson(input) {
332
577
  }
333
578
  return (await response.json());
334
579
  }
580
+ function getAppliedSequence(nextSequence) {
581
+ return nextSequence - 1;
582
+ }
335
583
  async function sendHeartbeat(input) {
336
- await postJson({
584
+ return await postJson({
337
585
  apiBaseUrl: input.apiBaseUrl,
338
586
  sessionId: input.sessionId,
339
587
  token: input.token,
340
588
  path: `/api/remote-dev/sessions/${input.sessionId}/heartbeat`,
589
+ body: {
590
+ ...(input.clean !== undefined ? { clean: input.clean } : {}),
591
+ ...(input.lastSequence !== undefined ? { lastSequence: input.lastSequence } : {}),
592
+ },
593
+ });
594
+ }
595
+ async function checkpointRemoteDev(input) {
596
+ await postJson({
597
+ ...input,
598
+ path: `/api/remote-dev/sessions/${input.sessionId}/checkpoint`,
341
599
  body: {},
342
600
  });
601
+ process.stdout.write("checkpointed remote changes\n");
602
+ }
603
+ async function exitRemoteDev(input) {
604
+ const response = await fetch(`${trimTrailingSlash(input.apiBaseUrl)}/api/remote-dev/sessions/${input.sessionId}`, {
605
+ method: "DELETE",
606
+ headers: { authorization: `Bearer ${input.token}` },
607
+ });
608
+ if (!response.ok) {
609
+ throw new HttpError(await readErrorMessage(response), response.status);
610
+ }
611
+ }
612
+ async function sendFlushAck(input) {
613
+ await postJson({
614
+ apiBaseUrl: input.apiBaseUrl,
615
+ sessionId: input.sessionId,
616
+ token: input.token,
617
+ path: `/api/remote-dev/sessions/${input.sessionId}/flush-ack`,
618
+ body: {
619
+ requestId: input.requestId,
620
+ lastSequence: input.lastSequence,
621
+ },
622
+ });
343
623
  }
344
624
  async function getBinary(input) {
345
625
  const response = await fetch(`${trimTrailingSlash(input.apiBaseUrl)}${input.path}`, {
@@ -356,13 +636,74 @@ async function writeLocalConfig(root, config) {
356
636
  const configDirectory = path.join(root, ".vibable");
357
637
  await mkdir(configDirectory, { recursive: true });
358
638
  await writeFile(path.join(configDirectory, "config.json"), JSON.stringify(config, null, 2), { mode: 0o600 });
359
- await writeFile(path.join(configDirectory, "AGENTS.md"), [
360
- "# Vibable Remote Dev",
639
+ }
640
+ const vibableAgentsStartMarker = "<!-- VIBABLE_REMOTE_DEV_START -->";
641
+ const vibableAgentsEndMarker = "<!-- VIBABLE_REMOTE_DEV_END -->";
642
+ async function writeAgentInstructions(root, instructions) {
643
+ const configDirectory = path.join(root, ".vibable");
644
+ await mkdir(configDirectory, { recursive: true });
645
+ await writeFile(path.join(configDirectory, "AGENTS.md"), formatRemoteDevAgentsSection(instructions));
646
+ await writeFile(path.join(root, "AGENTS.md"), mergeRootAgentsInstructions(await readFile(path.join(root, "AGENTS.md"), "utf8").catch((error) => {
647
+ if (isMissingFileError(error)) {
648
+ return "";
649
+ }
650
+ throw error;
651
+ }), instructions));
652
+ }
653
+ function mergeRootAgentsInstructions(existingContent, instructions) {
654
+ const section = formatRemoteDevAgentsSection(instructions).trimEnd();
655
+ const nextSection = [
656
+ vibableAgentsStartMarker,
657
+ section,
658
+ vibableAgentsEndMarker,
659
+ ].join("\n");
660
+ const start = existingContent.indexOf(vibableAgentsStartMarker);
661
+ const end = existingContent.indexOf(vibableAgentsEndMarker);
662
+ if (start >= 0 && end > start) {
663
+ return `${existingContent.slice(0, start)}${nextSection}${existingContent.slice(end + vibableAgentsEndMarker.length)}`.trimEnd() + "\n";
664
+ }
665
+ const trimmed = existingContent.trimEnd();
666
+ return `${trimmed ? `${trimmed}\n\n` : ""}${nextSection}\n`;
667
+ }
668
+ function formatRemoteDevAgentsSection(instructions) {
669
+ return [
670
+ "# Vibable Local Development",
361
671
  "",
362
- "Use `vibable exec -- <command>` when a command must run in the remote feature container.",
672
+ "Only use `pnpm dlx @sand.ai/vibable-cli@latest exec -- <command>` when a command must run in the remote feature container, such as focused tests, type checks, builds, or runtime commands that cannot run locally.",
673
+ "Do not use remote execution for work that can be completed by reading or editing the synced local files.",
674
+ "Use an installed `vibable-*` Skill when available. If a Skill is missing or may be stale, run `pnpm dlx @sand.ai/vibable-cli@latest guide <skill>` to read the current public version.",
363
675
  "Do not sync or edit `.vibable/config.json` manually.",
364
676
  "",
365
- ].join("\n"));
677
+ instructions.trim(),
678
+ "",
679
+ ].join("\n");
680
+ }
681
+ export async function enableRemoteDevAppAuth(config) {
682
+ await postJson({
683
+ ...config,
684
+ path: `/api/remote-dev/sessions/${config.sessionId}/app-auth/enable`,
685
+ body: {},
686
+ });
687
+ process.stdout.write("Vibable App Auth enabled.\n");
688
+ }
689
+ export async function runRemoteDataMigration(input) {
690
+ const operationText = input.inputPath === "-"
691
+ ? await readStdin()
692
+ : await readFile(path.resolve(input.inputPath), "utf8");
693
+ const operation = JSON.parse(operationText);
694
+ const migration = await postJson({
695
+ ...input.config,
696
+ path: `/api/remote-dev/sessions/${input.config.sessionId}/data/migrations${input.wait ? "?wait=1" : ""}`,
697
+ body: operation,
698
+ });
699
+ process.stdout.write(`${JSON.stringify(migration, null, 2)}\n`);
700
+ }
701
+ async function readStdin() {
702
+ const chunks = [];
703
+ for await (const chunk of process.stdin) {
704
+ chunks.push(Buffer.isBuffer(chunk) ? chunk : Buffer.from(chunk));
705
+ }
706
+ return Buffer.concat(chunks).toString("utf8");
366
707
  }
367
708
  async function readLocalState(root) {
368
709
  try {