borgmcp 2.0.11 → 2.1.1

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 (58) hide show
  1. package/README.md +11 -3
  2. package/dist/assimilate-cmd.d.ts +22 -4
  3. package/dist/assimilate-cmd.d.ts.map +1 -1
  4. package/dist/assimilate-cmd.js +109 -110
  5. package/dist/assimilate-cmd.js.map +1 -1
  6. package/dist/assimilate-deps.d.ts +7 -1
  7. package/dist/assimilate-deps.d.ts.map +1 -1
  8. package/dist/assimilate-deps.js +69 -25
  9. package/dist/assimilate-deps.js.map +1 -1
  10. package/dist/claude.d.ts +3 -1
  11. package/dist/claude.d.ts.map +1 -1
  12. package/dist/claude.js +26 -12
  13. package/dist/claude.js.map +1 -1
  14. package/dist/cli-help.d.ts +2 -0
  15. package/dist/cli-help.d.ts.map +1 -1
  16. package/dist/cli-help.js +25 -7
  17. package/dist/cli-help.js.map +1 -1
  18. package/dist/config.d.ts +7 -4
  19. package/dist/config.d.ts.map +1 -1
  20. package/dist/config.js +21 -4
  21. package/dist/config.js.map +1 -1
  22. package/dist/remote-client.js +1 -1
  23. package/dist/remote-client.js.map +1 -1
  24. package/dist/repository-cube-init.d.ts +72 -0
  25. package/dist/repository-cube-init.d.ts.map +1 -0
  26. package/dist/repository-cube-init.js +341 -0
  27. package/dist/repository-cube-init.js.map +1 -0
  28. package/dist/repository-identity.d.ts +29 -0
  29. package/dist/repository-identity.d.ts.map +1 -0
  30. package/dist/repository-identity.js +143 -0
  31. package/dist/repository-identity.js.map +1 -0
  32. package/dist/server-errors.d.ts +17 -0
  33. package/dist/server-errors.d.ts.map +1 -1
  34. package/dist/server-errors.js +32 -0
  35. package/dist/server-errors.js.map +1 -1
  36. package/dist/server-facade.d.ts +11 -1
  37. package/dist/server-facade.d.ts.map +1 -1
  38. package/dist/server-facade.js +33 -3
  39. package/dist/server-facade.js.map +1 -1
  40. package/dist/server-handshake.d.ts +38 -4
  41. package/dist/server-handshake.d.ts.map +1 -1
  42. package/dist/server-handshake.js +175 -13
  43. package/dist/server-handshake.js.map +1 -1
  44. package/docs/EXTRACTION_PROVENANCE.md +9 -7
  45. package/docs/LOCAL_SERVER.md +44 -9
  46. package/docs/RELEASING.md +21 -4
  47. package/package.json +2 -2
  48. package/src/assimilate-cmd.ts +179 -137
  49. package/src/assimilate-deps.ts +96 -24
  50. package/src/claude.ts +34 -14
  51. package/src/cli-help.ts +28 -7
  52. package/src/config.ts +30 -8
  53. package/src/remote-client.ts +1 -1
  54. package/src/repository-cube-init.ts +452 -0
  55. package/src/repository-identity.ts +199 -0
  56. package/src/server-errors.ts +41 -0
  57. package/src/server-facade.ts +47 -2
  58. package/src/server-handshake.ts +258 -14
package/README.md CHANGED
@@ -60,8 +60,16 @@ From inside your project repo:
60
60
  borg assimilate
61
61
  ```
62
62
 
63
- You will be prompted for the self-hosted server to connect to. Borg derives a cube name from the repo, creates or joins that cube, registers the current session as a drone, and launches the selected agent CLI with cube context.
64
- Cube names use lowercase letters, digits, and hyphens, up to 64 characters. Use `--cube-name <name>` if you need to override the derived name. If the repository has no usable `origin`, Borg proposes the sanitized repository-directory name and asks for confirmation; pass `--yes` to accept it non-interactively.
63
+ You will be prompted for the self-hosted server to connect to. Borg first resolves an existing repository association. Without one, an exact-name accessible cube requires explicit interactive confirmation before Borg associates it with this repository; otherwise Borg shows the repository context, lets you edit the proposed cube name, offers the Software Development and Starter templates, and asks for creation confirmation. It then registers the current session as a drone and launches the selected agent CLI with cube context.
64
+ Cube names allow letters, digits, spaces, dots, underscores, and hyphens, start with a letter or digit, and are at most 120 UTF-8 bytes. Use `--cube-name <name>` to supply one directly. Pass `--yes` to accept the repository name and Software Development template non-interactively only when creating a new cube; it never adopts an existing cube by name.
65
+
66
+ To initialize the repository cube without creating a drone or launching an agent:
67
+
68
+ ```bash
69
+ borg server cube init
70
+ ```
71
+
72
+ Both commands use the same idempotent repository association. Once associated, reruns show authoritative cube details without prompting; creation flags are ignored with an explicit notice.
65
73
 
66
74
  To connect directly to a local server (non-interactive):
67
75
 
@@ -91,7 +99,7 @@ borg assimilate
91
99
 
92
100
  Setup provisions the first same-machine owner credential in the owner-only
93
101
  `~/.borg/credentials` file. Bare assimilation uses that credential without an
94
- invitation prompt, creates or joins the repository cube, attaches a drone to a
102
+ invitation prompt, creates or joins the Software Development repository cube, attaches a drone to a
95
103
  role seat, and launches the selected agent. For another client or device, run
96
104
  `borg server invite`, then use `borg assimilate --host <server> --enroll` on the
97
105
  intended recipient and enter the invitation at its hidden prompt.
@@ -7,6 +7,9 @@ import type { ExpectedBinding, FinalizeServerSeatOutcome, PersistedLocalSeat } f
7
7
  import type { SeatBinding } from './seats.js';
8
8
  import { type LaunchApprovalDecision } from './cli-tool-approval.js';
9
9
  import { type WorkingRepo } from './working-repo.js';
10
+ import type { AssociateRepositoryCubeResponse, CreateCubeRepository, CubeTemplate } from 'borgmcp-shared/protocol';
11
+ import { type RepositoryCubeCreation, type RepositoryCubeResolution } from './repository-cube-init.js';
12
+ import type { GitRepositoryContext, RepositoryAssociation } from './repository-identity.js';
10
13
  export interface AssimilateFlags {
11
14
  worktree?: string;
12
15
  template?: string;
@@ -22,6 +25,7 @@ export interface AssimilateFlags {
22
25
  export interface AssimilateArgs {
23
26
  role: string | undefined;
24
27
  flags: AssimilateFlags;
28
+ mode?: 'assimilate' | 'cube-init';
25
29
  }
26
30
  export interface CubeSummary {
27
31
  id: string;
@@ -130,6 +134,10 @@ export interface AssimilateDeps {
130
134
  scrubPending: () => Promise<unknown>;
131
135
  }) => Promise<FinalizeServerSeatOutcome>;
132
136
  findProjectRoot: (cwd: string) => string;
137
+ resolveRepositoryContext: (cwd: string) => Promise<GitRepositoryContext | null>;
138
+ getRepositoryIdentity: (context: GitRepositoryContext) => Promise<CreateCubeRepository>;
139
+ getRepositoryAssociation: (trustIdentity: string, repository: CreateCubeRepository) => Promise<RepositoryAssociation | null>;
140
+ saveRepositoryAssociation: (trustIdentity: string, repository: CreateCubeRepository, association: RepositoryAssociation) => Promise<void>;
133
141
  installProjectSessionHook: (projectRoot: string) => void;
134
142
  /** gh#27: optional test seam — when set, selectAssimilationAuthority uses
135
143
  * this instead of prompting/failing. Not wired in production. */
@@ -147,13 +155,23 @@ export interface AssimilateDeps {
147
155
  trustIdentity: string;
148
156
  serverCapabilities?: readonly string[];
149
157
  } | null>;
158
+ resolveRepositoryCube: (apiUrl: string, token: string, input: {
159
+ workingRepoName: string;
160
+ repository: CreateCubeRepository;
161
+ }, serverTrustIdentity?: string) => Promise<RepositoryCubeResolution>;
162
+ associateRepositoryCube: (apiUrl: string, token: string, input: {
163
+ cubeId: string;
164
+ workingRepoName: string;
165
+ repository: CreateCubeRepository;
166
+ }, serverTrustIdentity?: string) => Promise<AssociateRepositoryCubeResponse>;
150
167
  listCubes: (apiUrl: string, token: string, serverTrustIdentity?: string) => Promise<CubeSummary[]>;
151
168
  getCube: (apiUrl: string, token: string, cubeId: string, serverTrustIdentity?: string) => Promise<CubeDetail>;
152
169
  createCube: (apiUrl: string, token: string, params: {
153
- name?: string;
154
- template?: string;
155
- projectRoot?: string;
156
- }, serverTrustIdentity?: string) => Promise<CubeDetail>;
170
+ name: string;
171
+ workingRepoName: string;
172
+ repository: CreateCubeRepository;
173
+ template: Exclude<CubeTemplate, 'default'>;
174
+ }, serverTrustIdentity?: string) => Promise<RepositoryCubeCreation>;
157
175
  assimilate: (apiUrl: string, token: string, params: {
158
176
  cube_id: string;
159
177
  role_id: string;
@@ -1 +1 @@
1
- {"version":3,"file":"assimilate-cmd.d.ts","sourceRoot":"","sources":["../src/assimilate-cmd.ts"],"names":[],"mappings":"AAEA,OAAO,KAAK,EAAE,IAAI,EAAE,YAAY,EAAE,MAAM,oBAAoB,CAAC;AAW7D,OAAO,EAAmB,KAAK,iBAAiB,EAAE,MAAM,mBAAmB,CAAC;AAe5E,OAAO,KAAK,EAAE,OAAO,EAAE,MAAM,YAAY,CAAC;AAiB1C,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,iBAAiB,CAAC;AAClD,OAAO,KAAK,EAAE,sBAAsB,EAAE,MAAM,aAAa,CAAC;AAC1D,OAAO,KAAK,EAAE,eAAe,EAAE,yBAAyB,EAAE,kBAAkB,EAAE,MAAM,YAAY,CAAC;AACjG,OAAO,KAAK,EAAE,WAAW,EAA0B,MAAM,YAAY,CAAC;AAEtE,OAAO,EAA2B,KAAK,sBAAsB,EAAE,MAAM,wBAAwB,CAAC;AAC9F,OAAO,EAAsB,KAAK,WAAW,EAAE,MAAM,mBAAmB,CAAC;AAQzE,MAAM,WAAW,eAAe;IAC9B,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,UAAU,CAAC,EAAE,OAAO,CAAC;IACrB,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,IAAI,CAAC,EAAE,OAAO,CAAC;IACf,GAAG,CAAC,EAAE,OAAO,CAAC;IACd,GAAG,CAAC,EAAE,OAAO,CAAC;IACd,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,MAAM,CAAC,EAAE,OAAO,CAAC;CAClB;AAED,MAAM,WAAW,cAAc;IAC7B,IAAI,EAAE,MAAM,GAAG,SAAS,CAAC;IACzB,KAAK,EAAE,eAAe,CAAC;CACxB;AAED,MAAM,WAAW,WAAW;IAC1B,EAAE,EAAE,MAAM,CAAC;IACX,IAAI,EAAE,MAAM,CAAC;CACd;AAED,MAAM,WAAW,UAAU;IACzB,EAAE,EAAE,MAAM,CAAC;IACX,IAAI,EAAE,MAAM,CAAC;IACb,KAAK,EAAE,IAAI,EAAE,CAAC;IAKd,MAAM,CAAC,EAAE,YAAY,EAAE,CAAC;CACzB;AAED,MAAM,WAAW,gBAAgB;IAC/B,OAAO,EAAE,MAAM,CAAC;IAChB,QAAQ,EAAE,MAAM,CAAC;IACjB,WAAW,EAAE,MAAM,CAAC;IACpB,aAAa,CAAC,EAAE,MAAM,CAAC;IACvB,OAAO,EAAE,MAAM,CAAC;IAChB,aAAa,CAAC,EAAE;QACd,cAAc,EAAE,MAAM,CAAC;KACxB,CAAC;IAGF,MAAM,CAAC,EAAE,SAAS,GAAG,QAAQ,CAAC;IAK9B,QAAQ,CAAC,EAAE;QACT,QAAQ,EAAE,CAAC,OAAO,EAAE,WAAW,KAAK,OAAO,CAAC,OAAO,CAAC,CAAC;QACrD,YAAY,EAAE,MAAM,OAAO,CAAC,OAAO,CAAC,CAAC;QAKrC,WAAW,CAAC,EAAE,CAAC,OAAO,EAAE,WAAW,KAAK,OAAO,CAAC,OAAO,CAAC,CAAC;KAC1D,CAAC;IAIF,cAAc,CAAC,EAAE,OAAO,CAAC;CAC1B;AAED,MAAM,WAAW,UAAU;IACzB,MAAM,EAAE,MAAM,CAAC;IACf,OAAO,EAAE,MAAM,CAAC;IAChB,IAAI,EAAE,MAAM,CAAC;IACb,YAAY,CAAC,EAAE,MAAM,CAAC;IACtB,UAAU,EAAE,MAAM,CAAC;IACnB,MAAM,EAAE,MAAM,CAAC;IACf,kFAAkF;IAClF,mBAAmB,CAAC,EAAE,MAAM,CAAC;IAC7B,yBAAyB,CAAC,EAAE,MAAM,CAAC;IAGnC,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,SAAS,CAAC,EAAE,OAAO,GAAG,QAAQ,CAAC;IAC/B,WAAW,CAAC,EAAE,OAAO,CAAC;CACvB;AAED,MAAM,WAAW,cAAc;IAC7B,OAAO,EAAE,CAAC,GAAG,EAAE,MAAM,EAAE,IAAI,EAAE,MAAM,EAAE,EAAE,GAAG,CAAC,EAAE,MAAM,KAAK;QAAE,MAAM,EAAE,MAAM,GAAG,IAAI,CAAC;QAAC,MAAM,EAAE,MAAM,CAAC;QAAC,MAAM,EAAE,MAAM,CAAA;KAAE,CAAC;IAClH,UAAU,EAAE,CAAC,CAAC,EAAE,MAAM,KAAK,OAAO,CAAC;IACnC,GAAG,EAAE,MAAM,MAAM,CAAC;IAClB,KAAK,EAAE,CAAC,CAAC,EAAE,MAAM,KAAK,IAAI,CAAC;IAM3B,OAAO,EAAE,MAAM,MAAM,CAAC;IACtB,MAAM,EAAE,CAAC,GAAG,EAAE,MAAM,KAAK,IAAI,CAAC;IAC9B,kBAAkB,EAAE,MAAM,OAAO,CAAC,IAAI,CAAC,CAAC;IACxC,IAAI,EAAE,CAAC,GAAG,EAAE,MAAM,EAAE,IAAI,EAAE,MAAM,EAAE,EAAE,GAAG,EAAE,MAAM,EAAE,GAAG,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,KAAK,OAAO,CAAC,MAAM,CAAC,CAAC;IAElG,MAAM,EAAE,CAAC,IAAI,EAAE,MAAM,KAAK,IAAI,CAAC;IAC/B,MAAM,EAAE,CAAC,IAAI,EAAE,MAAM,KAAK,IAAI,CAAC;IAC/B,MAAM,EAAE,CAAC,OAAO,EAAE,MAAM,KAAK,OAAO,CAAC,MAAM,CAAC,CAAC;IAC7C,YAAY,EAAE,CAAC,OAAO,EAAE,MAAM,KAAK,OAAO,CAAC,MAAM,CAAC,CAAC;IACnD,KAAK,EAAE,MAAM,OAAO,CAAC;IACrB,gEAAgE;IAChE,mBAAmB,CAAC,EAAE,CAAC,GAAG,EAAE,OAAO,EAAE,GAAG,EAAE,MAAM,KAAK,OAAO,CAAC,sBAAsB,CAAC,CAAC;IAMrF,WAAW,EAAE,MAAM,MAAM,CAAC;IAO1B,gBAAgB,EAAE,CAAC,KAAK,EAAE,MAAM,EAAE,QAAQ,EAAE,MAAM,KAAK,IAAI,CAAC;IAM5D,aAAa,EAAE,MAAM,OAAO,CAAC,UAAU,GAAG,IAAI,CAAC,CAAC;IAChD,sBAAsB,EAAE,MAAM,OAAO,CAAC,OAAO,CAAC,CAAC;IAC/C;;;sFAGkF;IAClF,sBAAsB,CAAC,EAAE,MAAM,OAAO,CAAC,kBAAkB,GAAG,IAAI,CAAC,CAAC;IAClE;;+DAE2D;IAC3D,uBAAuB,CAAC,EAAE,CACxB,aAAa,EAAE,MAAM,EACrB,OAAO,EAAE;QAAE,MAAM,EAAE,MAAM,CAAC;QAAC,aAAa,EAAE,MAAM,CAAC;QAAC,MAAM,EAAE,MAAM,CAAA;KAAE,KAC/D,OAAO,CAAC,OAAO,CAAC,CAAC;IACtB;;;;mCAI+B;IAC/B,4BAA4B,CAAC,EAAE,CAAC,OAAO,EAAE;QACvC,MAAM,EAAE,MAAM,CAAC;QACf,aAAa,EAAE,MAAM,CAAC;QACtB,MAAM,EAAE,MAAM,CAAC;QACf,WAAW,EAAE,MAAM,CAAC;KACrB,KAAK,OAAO,CAAC;QAAE,SAAS,EAAE,sBAAsB,CAAC;QAAC,MAAM,EAAE,MAAM,CAAC;QAAC,aAAa,EAAE,MAAM,CAAA;KAAE,GAAG,IAAI,CAAC,CAAC;IACnG,SAAS,EAAE,CACT,YAAY,EAAE,MAAM,EACpB,MAAM,EAAE,MAAM,EACd,mBAAmB,CAAC,EAAE,MAAM,KACzB,OAAO,CAAC,UAAU,CAAC,CAAC;IACzB,aAAa,EAAE,CAAC,CAAC,EAAE,UAAU,KAAK,OAAO,CAAC,IAAI,CAAC,CAAC;IAChD;;;;2EAIuE;IACvE,kBAAkB,CAAC,EAAE,CAAC,KAAK,EAAE;QAC3B,MAAM,EAAE,UAAU,CAAC;QACnB,QAAQ,EAAE,eAAe,CAAC;QAC1B,QAAQ,EAAE,CAAC,OAAO,EAAE,WAAW,KAAK,OAAO,CAAC,OAAO,CAAC,CAAC;QACrD,YAAY,EAAE,MAAM,OAAO,CAAC,OAAO,CAAC,CAAC;KACtC,KAAK,OAAO,CAAC,yBAAyB,CAAC,CAAC;IACzC,eAAe,EAAE,CAAC,GAAG,EAAE,MAAM,KAAK,MAAM,CAAC;IAKzC,yBAAyB,EAAE,CAAC,WAAW,EAAE,MAAM,KAAK,IAAI,CAAC;IAEzD;sEACkE;IAClE,gBAAgB,CAAC,EAAE,qBAAqB,CAAC;IACzC,iBAAiB,EAAE,MAAM,OAAO,CAAC,MAAM,GAAG,IAAI,CAAC,CAAC;IAChD,aAAa,EAAE,CACb,MAAM,EAAE,MAAM,EACd,UAAU,CAAC,EAAE;QAAE,UAAU,EAAE,MAAM,CAAA;KAAE,KAChC,OAAO,CAAC;QACX,KAAK,EAAE,MAAM,CAAC;QACd,aAAa,EAAE,MAAM,CAAC;QACtB,kBAAkB,CAAC,EAAE,SAAS,MAAM,EAAE,CAAC;KACxC,CAAC,CAAC;IACH,sBAAsB,EAAE,CACtB,MAAM,EAAE,MAAM,EACd,SAAS,CAAC,EAAE,MAAM,IAAI,KACnB,OAAO,CAAC;QACX,KAAK,EAAE,MAAM,CAAC;QACd,aAAa,EAAE,MAAM,CAAC;QACtB,kBAAkB,CAAC,EAAE,SAAS,MAAM,EAAE,CAAC;KACxC,GAAG,IAAI,CAAC,CAAC;IAEV,SAAS,EAAE,CAAC,MAAM,EAAE,MAAM,EAAE,KAAK,EAAE,MAAM,EAAE,mBAAmB,CAAC,EAAE,MAAM,KAAK,OAAO,CAAC,WAAW,EAAE,CAAC,CAAC;IACnG,OAAO,EAAE,CAAC,MAAM,EAAE,MAAM,EAAE,KAAK,EAAE,MAAM,EAAE,MAAM,EAAE,MAAM,EAAE,mBAAmB,CAAC,EAAE,MAAM,KAAK,OAAO,CAAC,UAAU,CAAC,CAAC;IAC9G,UAAU,EAAE,CACV,MAAM,EAAE,MAAM,EACd,KAAK,EAAE,MAAM,EACb,MAAM,EAAE;QAAE,IAAI,CAAC,EAAE,MAAM,CAAC;QAAC,QAAQ,CAAC,EAAE,MAAM,CAAC;QAAC,WAAW,CAAC,EAAE,MAAM,CAAA;KAAE,EAClE,mBAAmB,CAAC,EAAE,MAAM,KACzB,OAAO,CAAC,UAAU,CAAC,CAAC;IACzB,UAAU,EAAE,CACV,MAAM,EAAE,MAAM,EACd,KAAK,EAAE,MAAM,EACb,MAAM,EAAE;QAAE,OAAO,EAAE,MAAM,CAAC;QAAC,OAAO,EAAE,MAAM,CAAC;QAAC,QAAQ,CAAC,EAAE,MAAM,GAAG,IAAI,CAAC;QAAC,cAAc,CAAC,EAAE,MAAM,CAAC;QAAC,oBAAoB,CAAC,EAAE,OAAO,CAAC;QAAC,KAAK,CAAC,EAAE,MAAM,GAAG,IAAI,CAAC;QAAC,UAAU,CAAC,EAAE,QAAQ,GAAG,OAAO,GAAG,UAAU,GAAG,IAAI,CAAC;QAAC,YAAY,CAAC,EAAE,WAAW,CAAC;QAAC,iBAAiB,CAAC,EAAE,sBAAsB,CAAC;QAAC,gBAAgB,CAAC,EAAE,eAAe,CAAC;QAAC,qBAAqB,CAAC,EAAE,OAAO,CAAA;KAAE,EACxV,mBAAmB,CAAC,EAAE,MAAM,KACzB,OAAO,CAAC,gBAAgB,CAAC,CAAC;IAM/B,YAAY,EAAE,CAAC,MAAM,EAAE,MAAM,EAAE,OAAO,EAAE,MAAM,KAAK,MAAM,CAAC;IAY1D,aAAa,EAAE,MAAM,OAAO,CAAC,OAAO,CAAC,CAAC;IACtC,UAAU,EAAE,CAAC,QAAQ,CAAC,EAAE,OAAO,KAAK,OAAO,CAAC,OAAO,CAAC,CAAC;IACrD,wBAAwB,EAAE,MAAM,OAAO,CAAC,iBAAiB,CAAC,CAAC;IAC3D,kBAAkB,EAAE,CAAC,MAAM,EAAE,MAAM,EAAE,OAAO,EAAE,MAAM,EAAE,MAAM,EAAE;QAAE,QAAQ,EAAE,MAAM,CAAC;QAAC,UAAU,EAAE,MAAM,CAAA;KAAE,KAAK,OAAO,CAAC,IAAI,CAAC,CAAC;IACzH,qBAAqB,EAAE,CAAC,OAAO,EAAE;QAC/B,UAAU,EAAE,MAAM,CAAC;QACnB,GAAG,EAAE,MAAM,CAAC;QACZ,eAAe,EAAE,MAAM,CAAC;QACxB,YAAY,EAAE,MAAM,CAAC;KACtB,KAAK,OAAO,CAAC,MAAM,GAAG,IAAI,CAAC,CAAC;CAE9B;AAED,KAAK,qBAAqB,GACtB;IAAE,IAAI,EAAE,QAAQ,CAAC;IAAC,MAAM,EAAE,MAAM,CAAA;CAAE,CAAC;AAmNvC,wBAAsB,aAAa,CACjC,IAAI,EAAE,cAAc,EACpB,IAAI,EAAE,cAAc,GACnB,OAAO,CAAC,MAAM,CAAC,CAgwCjB;AAoBD;;;;;;;;;;;;;GAaG;AACH,wBAAgB,UAAU,CAAC,GAAG,EAAE,MAAM,GAAG,MAAM,CAE9C;AAQD;;;;;;;;;GASG;AACH,wBAAgB,eAAe,CAAC,KAAK,EAAE,MAAM,EAAE,UAAU,EAAE,MAAM,EAAE,GAAG,MAAM,GAAG,IAAI,CAWlF"}
1
+ {"version":3,"file":"assimilate-cmd.d.ts","sourceRoot":"","sources":["../src/assimilate-cmd.ts"],"names":[],"mappings":"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"}
@@ -1,7 +1,6 @@
1
1
  import { dirname, basename } from 'node:path';
2
2
  import { randomUUID } from 'node:crypto';
3
3
  import { roleSlug, matchRoleByName, occupiedRoleIdsForAutoRole, pickDefaultRole, } from './role-resolver.js';
4
- import { deriveCubeName, parseGitRemote, sanitizeRemoteUrl } from './cube-name.js';
5
4
  import { validateName } from './name-validator.js';
6
5
  import { renderAssimilationWelcome } from './assimilate-welcome.js';
7
6
  import { shellEscape } from './shell-escape.js';
@@ -20,10 +19,11 @@ import { installBorgPlugin } from './opencode-plugin.js';
20
19
  import { computeOpenCodePort, connectOpenCodeDrone, createOpenCodeLaunchKickoff, injectInitialKickoff } from './opencode-drone.js';
21
20
  import { ensureCliMcpConfigured } from './ensure-mcp-config.js';
22
21
  import { normalizeServerEndpoint } from './server-endpoint.js';
23
- import { BorgServerError, LegacySessionCredentialCollisionError } from './server-errors.js';
22
+ import { BorgServerError, CubeCreationConfirmationError, CubeCreationOutcomeUnknownError, LegacySessionCredentialCollisionError, RepositoryAssociationOperationError, RepositoryAssociationOutcomeUnknownError, RepositoryAssociationResolutionError, } from './server-errors.js';
24
23
  import { createHash } from 'node:crypto';
25
24
  import { buildOpenCodeLaunchArgs } from './cli-tool-approval.js';
26
25
  import { resolveWorkingRepo } from './working-repo.js';
26
+ import { initializeRepositoryCube, RepositoryAssociationConfirmationError, RepositoryAssociationSaveError, validRepositoryCubeName, } from './repository-cube-init.js';
27
27
  const PRIVATE_STATE_UNAVAILABLE_COPY = [
28
28
  'Borg could not safely prepare its private local state.',
29
29
  'No Borg server or cube change was made.',
@@ -33,10 +33,6 @@ function affirmative(answer) {
33
33
  const normalized = answer.trim().toLowerCase();
34
34
  return normalized === '' || normalized === 'y' || normalized === 'yes';
35
35
  }
36
- function isLocalCubePresentationName(name) {
37
- return name.length >= 1 && name.length <= 120 &&
38
- /^[A-Za-z0-9][A-Za-z0-9 ._-]*$/.test(name);
39
- }
40
36
  async function selectAssimilationAuthority(flags, deps) {
41
37
  if (flags.server !== undefined) {
42
38
  try {
@@ -191,6 +187,7 @@ function diagnoseSessionTermination(deps, apiUrl, outcome) {
191
187
  return 1;
192
188
  }
193
189
  export async function runAssimilate(args, deps) {
190
+ const mode = args.mode ?? 'assimilate';
194
191
  // ----- Input validation (before any subprocess work) -----
195
192
  if (args.role !== undefined) {
196
193
  const v = validateName(args.role);
@@ -206,6 +203,27 @@ export async function runAssimilate(args, deps) {
206
203
  return 1;
207
204
  }
208
205
  }
206
+ if (args.flags.cubeName !== undefined && !validRepositoryCubeName(args.flags.cubeName.trim())) {
207
+ deps.stderr('Invalid cube name. Use 1-120 letters, digits, spaces, dots, underscores, or hyphens, starting with a letter or digit.\n');
208
+ return 1;
209
+ }
210
+ let repositoryContext;
211
+ try {
212
+ repositoryContext = await deps.resolveRepositoryContext(deps.cwd());
213
+ }
214
+ catch (error) {
215
+ if (error instanceof Error && error.message === 'BARE_REPOSITORY') {
216
+ deps.stderr('borg assimilate requires a non-bare repository worktree. Clone or check out the repository, then retry.\n');
217
+ return 1;
218
+ }
219
+ repositoryContext = null;
220
+ }
221
+ if (!repositoryContext) {
222
+ deps.stderr('No Git repository was found for this directory.\n' +
223
+ 'Nothing was changed.\n' +
224
+ 'Run this command inside a Git repository.\n');
225
+ return 1;
226
+ }
209
227
  try {
210
228
  await deps.preparePrivateRoot();
211
229
  }
@@ -236,67 +254,7 @@ export async function runAssimilate(args, deps) {
236
254
  if (localSeatReadError !== undefined) {
237
255
  return reportServerFailure(deps, authority.apiUrl, localSeatReadError);
238
256
  }
239
- // ----- Repository + cube-name preflight -----
240
- // Resolve and, where necessary, confirm local presentation data before an
241
- // owner invitation can be consumed. A declined/underivable basename must
242
- // not leave a successfully enrolled client behind.
243
- const projectRoot = deps.findProjectRoot(deps.cwd());
244
- let cubeName;
245
- if (args.flags.cubeName) {
246
- cubeName = args.flags.cubeName;
247
- }
248
- else {
249
- const remoteResult = deps.runSync('git', ['remote', 'get-url', 'origin'], projectRoot);
250
- const remoteUrl = remoteResult.status === 0 ? remoteResult.stdout : null;
251
- const sanitizedRemote = remoteUrl ? sanitizeRemoteUrl(remoteUrl) : null;
252
- const parsedRepo = sanitizedRemote ? parseGitRemote(sanitizedRemote) : null;
253
- if (!parsedRepo) {
254
- const bareResult = deps.runSync('git', ['rev-parse', '--is-bare-repository'], projectRoot);
255
- if (bareResult.status === 0 && bareResult.stdout.trim() === 'true') {
256
- deps.stderr('borg assimilate requires a non-bare repository worktree. ' +
257
- (authority.kind === 'server'
258
- ? `Clone or check out the repository, then rerun ${localAssimilateCommand(authority.apiUrl)}.\n`
259
- : 'Clone or check out the repository, then retry.\n'));
260
- return 1;
261
- }
262
- }
263
- cubeName = deriveCubeName(projectRoot, remoteUrl);
264
- if (!cubeName) {
265
- deps.stderr('Could not derive a cube name from this repository. ' +
266
- (authority.kind === 'server'
267
- ? `Rerun ${localAssimilateCommand(authority.apiUrl)} with \`--cube-name <name>\`.\n`
268
- : 'Pass --cube-name <name> and retry.\n'));
269
- return 1;
270
- }
271
- if (!parsedRepo) {
272
- if (sanitizedRemote) {
273
- deps.stderr(`Could not parse the origin remote; using directory name '${cubeName}' as the cube name.\n`);
274
- }
275
- if (!args.flags.yes) {
276
- if (!deps.isTTY()) {
277
- deps.stderr(`Using directory name '${cubeName}' as the cube name requires confirmation. ` +
278
- (authority.kind === 'server'
279
- ? `Rerun ${localAssimilateCommand(authority.apiUrl)} with \`--cube-name <name>\` or \`--yes\`.\n`
280
- : 'Re-run with --cube-name <name> or --yes.\n'));
281
- return 1;
282
- }
283
- const confirmed = await deps.prompt(`No usable origin remote was found. Use directory name '${cubeName}' as the cube name? [Y/n]: `);
284
- if (!affirmative(confirmed)) {
285
- deps.stderr(authority.kind === 'server'
286
- ? `Cube creation for ${authority.apiUrl} was cancelled. Rerun ` +
287
- `${localAssimilateCommand(authority.apiUrl)} with \`--cube-name <name>\`.\n`
288
- : 'Cube creation cancelled. Re-run with --cube-name <name> to choose a name.\n');
289
- return 1;
290
- }
291
- }
292
- }
293
- }
294
- if (authority.kind === 'server' && !isLocalCubePresentationName(cubeName)) {
295
- deps.stderr(`Invalid cube name for ${authority.apiUrl}. Use 1–120 letters, digits, spaces, dots, ` +
296
- 'underscores, or hyphens, starting with a letter or digit. Rerun ' +
297
- `${localAssimilateCommand(authority.apiUrl)} with \`--cube-name <name>\`.\n`);
298
- return 1;
299
- }
257
+ const projectRoot = repositoryContext.root;
300
258
  let auth;
301
259
  {
302
260
  try {
@@ -347,6 +305,7 @@ export async function runAssimilate(args, deps) {
347
305
  token: serverAuth.token,
348
306
  apiUrl: authority.apiUrl,
349
307
  serverTrustIdentity: serverAuth.trustIdentity,
308
+ serverCapabilities: serverAuth.serverCapabilities ?? [],
350
309
  };
351
310
  }
352
311
  catch (error) {
@@ -366,57 +325,97 @@ export async function runAssimilate(args, deps) {
366
325
  // Sprint 18: capture pre-chdir cwd for the post-exit shell-cd hint
367
326
  // (no chdir has happened yet; this is a stable starting point).
368
327
  const originalCwd = deps.cwd();
369
- // ----- Step 3: Cube existence check (with auto-refresh on auth failure) -----
370
- // gh#653 B4: announce each network step. These calls take 2–5s and were
371
- // previously silent, so a user read the wait as a hang and Ctrl-C'd mid-run.
372
- deps.stderr('Checking your cubes…\n');
373
- let allCubes;
328
+ let initialized;
374
329
  try {
375
- allCubes = await deps.listCubes(auth.apiUrl, auth.token, auth.serverTrustIdentity);
376
- }
377
- catch (err) {
378
- return reportServerFailure(deps, authority.apiUrl, err);
330
+ initialized = await initializeRepositoryCube({
331
+ mode,
332
+ context: repositoryContext,
333
+ serverOrigin: auth.apiUrl,
334
+ flags: args.flags,
335
+ canCreate: auth.serverCapabilities.includes('create_cube'),
336
+ }, {
337
+ isTTY: deps.isTTY,
338
+ prompt: deps.prompt,
339
+ write: deps.stderr,
340
+ getIdentity: deps.getRepositoryIdentity,
341
+ getAssociation: (repository) => deps.getRepositoryAssociation(auth.serverTrustIdentity, repository),
342
+ saveAssociation: (repository, association) => deps.saveRepositoryAssociation(auth.serverTrustIdentity, repository, association),
343
+ resolveAssociation: (repository, workingRepoName) => deps.resolveRepositoryCube(auth.apiUrl, auth.token, { repository, workingRepoName }, auth.serverTrustIdentity),
344
+ listCubes: () => deps.listCubes(auth.apiUrl, auth.token, auth.serverTrustIdentity),
345
+ associateCube: (params) => deps.associateRepositoryCube(auth.apiUrl, auth.token, params, auth.serverTrustIdentity),
346
+ getCube: (cubeId) => deps.getCube(auth.apiUrl, auth.token, cubeId, auth.serverTrustIdentity),
347
+ createCube: (params) => deps.createCube(auth.apiUrl, auth.token, params, auth.serverTrustIdentity),
348
+ });
379
349
  }
380
- const existingCube = allCubes.find((c) => c.name === cubeName);
381
- // ----- Step 4: Fetch detail OR create cube -----
382
- let cubeDetail;
383
- let isFirstDrone;
384
- if (existingCube) {
385
- try {
386
- cubeDetail = await deps.getCube(auth.apiUrl, auth.token, existingCube.id, auth.serverTrustIdentity);
350
+ catch (error) {
351
+ if (error instanceof RepositoryAssociationOutcomeUnknownError) {
352
+ deps.stderr('Repository cube association outcome is unknown.\n' +
353
+ 'The server may have created the repository binding; no local repository association was saved and no drone was created.\n' +
354
+ 'Run the same command again; Borg will first resolve the authoritative server association without creating a cube.\n');
355
+ return 1;
387
356
  }
388
- catch (error) {
389
- return reportServerFailure(deps, authority.apiUrl, error);
357
+ if (error instanceof RepositoryAssociationResolutionError) {
358
+ deps.stderr('Repository cube association could not be resolved.\n' +
359
+ 'Verify that the server is reachable and the client and server versions match, then run the same command again.\n' +
360
+ 'No cube, repository binding, or drone was created.\n');
361
+ return 1;
390
362
  }
391
- isFirstDrone = (cubeDetail.drones?.length ?? 0) === 0;
392
- }
393
- else {
394
- // ----- Step 4a: First-drone bootstrap (template selection) -----
395
- let chosenTemplate;
396
- if (args.flags.noTemplate ||
397
- (args.flags.template !== undefined && args.flags.template !== 'default')) {
398
- deps.stderr(`Borg server ${authority.apiUrl} supports its default cube template only. ` +
399
- `Rerun ${localAssimilateCommand(authority.apiUrl)} without \`--template\` or \`--no-template\`.\n`);
363
+ if (error instanceof RepositoryAssociationOperationError) {
364
+ const recovery = error.failure === 'repository-already-associated'
365
+ ? 'This repository is already associated with another cube. Run the same command again to use the existing managed association, or ask the server operator to correct the repository binding.'
366
+ : error.failure === 'cube-already-associated'
367
+ ? 'The selected cube is already associated with another repository. Choose a different cube, or run the command from the repository already linked to that cube.'
368
+ : error.failure === 'access-denied'
369
+ ? 'This enrolled client does not have permission to manage the selected cube. Ask the server operator to grant this client management access, then run the same command again.'
370
+ : 'The selected cube does not have valid authoritative roles. Ask the server operator to repair its role configuration, or choose another cube.';
371
+ deps.stderr('Repository cube association could not be completed.\n' +
372
+ `${recovery}\n` +
373
+ 'No cube, repository binding, or drone was created.\n');
400
374
  return 1;
401
375
  }
402
- chosenTemplate = 'default';
403
- // gh#653 B4: progress for the create round-trip (silent-window stall).
404
- deps.stderr(cubeName ? `Creating cube '${cubeName}'…\n` : 'Creating your cube…\n');
405
- try {
406
- const createParams = chosenTemplate
407
- ? {
408
- name: cubeName ?? undefined,
409
- template: chosenTemplate,
410
- projectRoot,
411
- }
412
- : { name: cubeName ?? undefined };
413
- cubeDetail = await deps.createCube(auth.apiUrl, auth.token, createParams, auth.serverTrustIdentity);
376
+ if (error instanceof RepositoryAssociationConfirmationError) {
377
+ deps.stderr('Repository cube association could not be confirmed.\n' +
378
+ 'The server may have created the repository binding; no local repository association was saved and no drone was created.\n' +
379
+ 'Run the same command again; Borg will resolve authoritative server state before creating or associating a cube.\n');
380
+ return 1;
414
381
  }
415
- catch (error) {
416
- return reportServerFailure(deps, authority.apiUrl, error);
382
+ if (error instanceof RepositoryAssociationSaveError) {
383
+ deps.stderr('The repository cube was confirmed, but Borg could not save its local repository association.\n' +
384
+ 'No drone was created.\n' +
385
+ 'Run the same command again; the server will resolve the existing cube and restore the local association.\n');
386
+ return 1;
417
387
  }
418
- isFirstDrone = true;
388
+ if (error instanceof CubeCreationOutcomeUnknownError) {
389
+ deps.stderr('Cube creation outcome is unconfirmed.\n' +
390
+ 'The server may have created the cube and repository binding; no local repository association was saved and no drone was created.\n' +
391
+ 'Run the same command again; Borg will resolve authoritative server state before creating a cube.\n');
392
+ return 1;
393
+ }
394
+ if (error instanceof CubeCreationConfirmationError) {
395
+ deps.stderr('Cube creation could not be confirmed.\n' +
396
+ 'The server may have created the cube and repository binding; no local repository association was saved and no drone was created.\n' +
397
+ 'Run the same command again; Borg will resolve authoritative server state before creating a cube.\n');
398
+ return 1;
399
+ }
400
+ if (error instanceof BorgServerError && error.code === 'CREATE_CUBE_DENIED') {
401
+ deps.stderr(`This enrolled client cannot create a cube on ${auth.apiUrl}. ` +
402
+ `Ask the server operator to grant access to a cube, then rerun ${localAssimilateCommand(auth.apiUrl)}.\n`);
403
+ return 1;
404
+ }
405
+ if (error instanceof BorgServerError) {
406
+ return reportServerFailure(deps, auth.apiUrl, error);
407
+ }
408
+ deps.stderr('Repository cube initialization failed.\n' +
409
+ 'The server may have created or associated a cube; local repository state may be incomplete and no drone was created.\n' +
410
+ 'Run the same command again; Borg will resolve authoritative server state before creating or associating a cube.\n');
411
+ return 1;
419
412
  }
413
+ if (initialized.kind === 'stop')
414
+ return initialized.code;
415
+ const cubeDetail = initialized.creation.cube;
416
+ const isFirstDrone = (cubeDetail.drones?.length ?? 0) === 0;
417
+ if (mode === 'cube-init')
418
+ return 0;
420
419
  // Read the worktree identity before role selection. A live local seat must
421
420
  // retain its original role so the attach request reuses the exact durable
422
421
  // retry binding instead of selecting another unoccupied role and minting a
@@ -1298,7 +1297,7 @@ export async function runAssimilate(args, deps) {
1298
1297
  serverUrl,
1299
1298
  directory: agentCwd,
1300
1299
  droneLabel: result.drone_label,
1301
- cubeName: cubeName ?? 'borg',
1300
+ cubeName: cubeDetail.name,
1302
1301
  })
1303
1302
  .then(() => injectInitialKickoff(launchKickoff))
1304
1303
  .catch(() => { });