borgmcp 2.0.11 → 2.1.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.
Files changed (57) hide show
  1. package/README.md +11 -3
  2. package/dist/assimilate-cmd.d.ts +13 -4
  3. package/dist/assimilate-cmd.d.ts.map +1 -1
  4. package/dist/assimilate-cmd.js +134 -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 +44 -18
  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 +22 -5
  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/repository-cube-init.d.ts +57 -0
  24. package/dist/repository-cube-init.d.ts.map +1 -0
  25. package/dist/repository-cube-init.js +196 -0
  26. package/dist/repository-cube-init.js.map +1 -0
  27. package/dist/repository-identity.d.ts +29 -0
  28. package/dist/repository-identity.d.ts.map +1 -0
  29. package/dist/repository-identity.js +143 -0
  30. package/dist/repository-identity.js.map +1 -0
  31. package/dist/server-errors.d.ts +6 -0
  32. package/dist/server-errors.d.ts.map +1 -1
  33. package/dist/server-errors.js +12 -0
  34. package/dist/server-errors.js.map +1 -1
  35. package/dist/server-facade.d.ts +11 -1
  36. package/dist/server-facade.d.ts.map +1 -1
  37. package/dist/server-facade.js +33 -3
  38. package/dist/server-facade.js.map +1 -1
  39. package/dist/server-handshake.d.ts +8 -4
  40. package/dist/server-handshake.d.ts.map +1 -1
  41. package/dist/server-handshake.js +15 -6
  42. package/dist/server-handshake.js.map +1 -1
  43. package/docs/EXTRACTION_PROVENANCE.md +7 -7
  44. package/docs/LOCAL_SERVER.md +35 -6
  45. package/docs/RELEASING.md +11 -3
  46. package/package.json +2 -2
  47. package/src/assimilate-cmd.ts +166 -140
  48. package/src/assimilate-deps.ts +59 -17
  49. package/src/claude.ts +34 -14
  50. package/src/cli-help.ts +25 -5
  51. package/src/config.ts +30 -8
  52. package/src/remote-client.ts +1 -1
  53. package/src/repository-cube-init.ts +273 -0
  54. package/src/repository-identity.ts +199 -0
  55. package/src/server-errors.ts +14 -0
  56. package/src/server-facade.ts +47 -2
  57. package/src/server-handshake.ts +33 -7
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 shows the repository context, lets you edit the proposed cube name, offers the Software Development and Starter templates, and asks for one confirmation. It then creates or resolves that repository's cube, 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.
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 { CreateCubeRepository, CubeTemplate } from 'borgmcp-shared/protocol';
11
+ import { type RepositoryCubeCreation } 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. */
@@ -150,10 +158,11 @@ export interface AssimilateDeps {
150
158
  listCubes: (apiUrl: string, token: string, serverTrustIdentity?: string) => Promise<CubeSummary[]>;
151
159
  getCube: (apiUrl: string, token: string, cubeId: string, serverTrustIdentity?: string) => Promise<CubeDetail>;
152
160
  createCube: (apiUrl: string, token: string, params: {
153
- name?: string;
154
- template?: string;
155
- projectRoot?: string;
156
- }, serverTrustIdentity?: string) => Promise<CubeDetail>;
161
+ name: string;
162
+ workingRepoName: string;
163
+ repository: CreateCubeRepository;
164
+ template: Exclude<CubeTemplate, 'default'>;
165
+ }, serverTrustIdentity?: string) => Promise<RepositoryCubeCreation>;
157
166
  assimilate: (apiUrl: string, token: string, params: {
158
167
  cube_id: string;
159
168
  role_id: string;
@@ -1 +1 @@
1
- {"version":3,"file":"assimilate-cmd.d.ts","sourceRoot":"","sources":["../src/assimilate-cmd.ts"],"names":[],"mappings":"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"}
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, } 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, 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,122 @@ 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);
379
- }
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);
330
+ if (!auth.serverCapabilities.includes('create_cube') && !existing) {
331
+ const repository = await deps.getRepositoryIdentity(repositoryContext);
332
+ const association = await deps.getRepositoryAssociation(auth.serverTrustIdentity, repository);
333
+ if (association) {
334
+ initialized = await initializeRepositoryCube({
335
+ mode, context: repositoryContext, serverOrigin: auth.apiUrl, flags: args.flags,
336
+ }, {
337
+ isTTY: deps.isTTY,
338
+ prompt: deps.prompt,
339
+ write: deps.stderr,
340
+ getIdentity: async () => repository,
341
+ getAssociation: async () => association,
342
+ saveAssociation: (identity, saved) => deps.saveRepositoryAssociation(auth.serverTrustIdentity, identity, saved),
343
+ getCube: (cubeId) => deps.getCube(auth.apiUrl, auth.token, cubeId, auth.serverTrustIdentity),
344
+ createCube: (params) => deps.createCube(auth.apiUrl, auth.token, params, auth.serverTrustIdentity),
345
+ });
346
+ }
347
+ else {
348
+ const candidateName = args.flags.cubeName ?? repositoryContext.derivedName;
349
+ const accessible = await deps.listCubes(auth.apiUrl, auth.token, auth.serverTrustIdentity);
350
+ const matches = accessible.filter((cube) => cube.name === candidateName);
351
+ if (matches.length !== 1) {
352
+ deps.stderr(matches.length === 0
353
+ ? `This enrolled client cannot create a cube on ${auth.apiUrl}. Ask the server operator to grant access to a cube, then rerun ${localAssimilateCommand(auth.apiUrl)}.\n`
354
+ : `More than one accessible cube is named '${candidateName}'. Nothing was changed. Ask the server operator for an unambiguous repository cube grant.\n`);
355
+ return 1;
356
+ }
357
+ const cube = await deps.getCube(auth.apiUrl, auth.token, matches[0].id, auth.serverTrustIdentity);
358
+ const association = {
359
+ cubeId: cube.id,
360
+ name: cube.name,
361
+ workingRepoName: repositoryContext.derivedName,
362
+ template: 'default',
363
+ };
364
+ try {
365
+ await deps.saveRepositoryAssociation(auth.serverTrustIdentity, repository, association);
366
+ }
367
+ catch {
368
+ throw new RepositoryAssociationSaveError();
369
+ }
370
+ const response = {
371
+ result: 'resolved',
372
+ cube_id: cube.id,
373
+ name: cube.name,
374
+ working_repo_name: repositoryContext.derivedName,
375
+ repository,
376
+ template: 'default',
377
+ human_seat_role_id: cube.roles.find((role) => role.is_human_seat)?.id ?? '',
378
+ default_worker_role_id: cube.roles.find((role) => role.is_default)?.id ?? '',
379
+ access: 'manage',
380
+ };
381
+ deps.stderr(`Cube already initialized.\n Name: ${cube.name}\n Template: Default (legacy)\n` +
382
+ ` Repository: ${repositoryContext.root}\n Server: ${auth.apiUrl}\n` +
383
+ (mode === 'cube-init'
384
+ ? `No drone was created.\nNext: borg assimilate --host ${shellEscape(auth.apiUrl)}\n`
385
+ : 'Continuing with role and seat setup...\n'));
386
+ initialized = { kind: 'success', creation: { response, cube }, existing: true };
387
+ }
387
388
  }
388
- catch (error) {
389
- return reportServerFailure(deps, authority.apiUrl, error);
389
+ else {
390
+ initialized = await initializeRepositoryCube({
391
+ mode,
392
+ context: repositoryContext,
393
+ serverOrigin: auth.apiUrl,
394
+ flags: args.flags,
395
+ }, {
396
+ isTTY: deps.isTTY,
397
+ prompt: deps.prompt,
398
+ write: deps.stderr,
399
+ getIdentity: deps.getRepositoryIdentity,
400
+ getAssociation: (repository) => deps.getRepositoryAssociation(auth.serverTrustIdentity, repository),
401
+ saveAssociation: (repository, association) => deps.saveRepositoryAssociation(auth.serverTrustIdentity, repository, association),
402
+ getCube: (cubeId) => deps.getCube(auth.apiUrl, auth.token, cubeId, auth.serverTrustIdentity),
403
+ createCube: (params) => deps.createCube(auth.apiUrl, auth.token, params, auth.serverTrustIdentity),
404
+ });
390
405
  }
391
- isFirstDrone = (cubeDetail.drones?.length ?? 0) === 0;
392
406
  }
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`);
407
+ catch (error) {
408
+ if (error instanceof RepositoryAssociationSaveError) {
409
+ deps.stderr('The repository cube was confirmed, but Borg could not save its local repository association.\n' +
410
+ 'No drone was created.\n' +
411
+ 'Run the same command again; the server will resolve the existing cube and restore the local association.\n');
400
412
  return 1;
401
413
  }
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);
414
+ if (error instanceof CubeCreationOutcomeUnknownError) {
415
+ deps.stderr('Cube creation outcome is unknown.\n' +
416
+ 'No local repository association was saved.\n' +
417
+ 'Run the same command again; the server will resolve the original request or return a conflict.\n');
418
+ return 1;
414
419
  }
415
- catch (error) {
416
- return reportServerFailure(deps, authority.apiUrl, error);
420
+ if (error instanceof CubeCreationConfirmationError) {
421
+ deps.stderr('Cube creation could not be confirmed.\n' +
422
+ `${error.message}\n` +
423
+ 'No local repository association was saved.\n' +
424
+ 'Resolve the conflict, then run the same command again.\n');
425
+ return 1;
426
+ }
427
+ if (error instanceof BorgServerError && error.code === 'CREATE_CUBE_DENIED') {
428
+ deps.stderr(`This enrolled client cannot create a cube on ${auth.apiUrl}. ` +
429
+ `Ask the server operator to grant access to a cube, then rerun ${localAssimilateCommand(auth.apiUrl)}.\n`);
430
+ return 1;
417
431
  }
418
- isFirstDrone = true;
432
+ if (error instanceof BorgServerError) {
433
+ return reportServerFailure(deps, auth.apiUrl, error);
434
+ }
435
+ deps.stderr('Could not create cube.\nThe selected Borg server rejected the request.\nNothing was changed.\n');
436
+ return 1;
419
437
  }
438
+ if (initialized.kind === 'stop')
439
+ return initialized.code;
440
+ const cubeDetail = initialized.creation.cube;
441
+ const isFirstDrone = (cubeDetail.drones?.length ?? 0) === 0;
442
+ if (mode === 'cube-init')
443
+ return 0;
420
444
  // Read the worktree identity before role selection. A live local seat must
421
445
  // retain its original role so the attach request reuses the exact durable
422
446
  // retry binding instead of selecting another unoccupied role and minting a
@@ -1298,7 +1322,7 @@ export async function runAssimilate(args, deps) {
1298
1322
  serverUrl,
1299
1323
  directory: agentCwd,
1300
1324
  droneLabel: result.drone_label,
1301
- cubeName: cubeName ?? 'borg',
1325
+ cubeName: cubeDetail.name,
1302
1326
  })
1303
1327
  .then(() => injectInitialKickoff(launchKickoff))
1304
1328
  .catch(() => { });