offwatch 0.5.11 → 0.5.13

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 (95) hide show
  1. package/README.md +132 -178
  2. package/bin/offwatch.js +6 -7
  3. package/lib/downloader.js +112 -0
  4. package/package.json +17 -7
  5. package/postinstall.js +21 -0
  6. package/src/__tests__/agent-jwt-env.test.ts +0 -79
  7. package/src/__tests__/allowed-hostname.test.ts +0 -80
  8. package/src/__tests__/auth-command-registration.test.ts +0 -16
  9. package/src/__tests__/board-auth.test.ts +0 -53
  10. package/src/__tests__/common.test.ts +0 -98
  11. package/src/__tests__/company-delete.test.ts +0 -95
  12. package/src/__tests__/company-import-export-e2e.test.ts +0 -502
  13. package/src/__tests__/company-import-url.test.ts +0 -74
  14. package/src/__tests__/company-import-zip.test.ts +0 -44
  15. package/src/__tests__/company.test.ts +0 -599
  16. package/src/__tests__/context.test.ts +0 -70
  17. package/src/__tests__/data-dir.test.ts +0 -79
  18. package/src/__tests__/doctor.test.ts +0 -102
  19. package/src/__tests__/feedback.test.ts +0 -177
  20. package/src/__tests__/helpers/embedded-postgres.ts +0 -6
  21. package/src/__tests__/helpers/zip.ts +0 -87
  22. package/src/__tests__/home-paths.test.ts +0 -44
  23. package/src/__tests__/http.test.ts +0 -106
  24. package/src/__tests__/network-bind.test.ts +0 -62
  25. package/src/__tests__/onboard.test.ts +0 -166
  26. package/src/__tests__/routines.test.ts +0 -249
  27. package/src/__tests__/telemetry.test.ts +0 -117
  28. package/src/__tests__/worktree-merge-history.test.ts +0 -492
  29. package/src/__tests__/worktree.test.ts +0 -982
  30. package/src/adapters/http/format-event.ts +0 -4
  31. package/src/adapters/http/index.ts +0 -7
  32. package/src/adapters/index.ts +0 -2
  33. package/src/adapters/process/format-event.ts +0 -4
  34. package/src/adapters/process/index.ts +0 -7
  35. package/src/adapters/registry.ts +0 -63
  36. package/src/checks/agent-jwt-secret-check.ts +0 -40
  37. package/src/checks/config-check.ts +0 -33
  38. package/src/checks/database-check.ts +0 -59
  39. package/src/checks/deployment-auth-check.ts +0 -88
  40. package/src/checks/index.ts +0 -18
  41. package/src/checks/llm-check.ts +0 -82
  42. package/src/checks/log-check.ts +0 -30
  43. package/src/checks/path-resolver.ts +0 -1
  44. package/src/checks/port-check.ts +0 -24
  45. package/src/checks/secrets-check.ts +0 -146
  46. package/src/checks/storage-check.ts +0 -51
  47. package/src/client/board-auth.ts +0 -282
  48. package/src/client/command-label.ts +0 -4
  49. package/src/client/context.ts +0 -175
  50. package/src/client/http.ts +0 -255
  51. package/src/commands/allowed-hostname.ts +0 -40
  52. package/src/commands/auth-bootstrap-ceo.ts +0 -138
  53. package/src/commands/client/activity.ts +0 -71
  54. package/src/commands/client/agent.ts +0 -315
  55. package/src/commands/client/approval.ts +0 -259
  56. package/src/commands/client/auth.ts +0 -113
  57. package/src/commands/client/common.ts +0 -221
  58. package/src/commands/client/company.ts +0 -1578
  59. package/src/commands/client/context.ts +0 -125
  60. package/src/commands/client/dashboard.ts +0 -34
  61. package/src/commands/client/feedback.ts +0 -645
  62. package/src/commands/client/issue.ts +0 -411
  63. package/src/commands/client/plugin.ts +0 -374
  64. package/src/commands/client/zip.ts +0 -129
  65. package/src/commands/configure.ts +0 -201
  66. package/src/commands/db-backup.ts +0 -102
  67. package/src/commands/doctor.ts +0 -203
  68. package/src/commands/env.ts +0 -411
  69. package/src/commands/heartbeat-run.ts +0 -344
  70. package/src/commands/onboard.ts +0 -692
  71. package/src/commands/routines.ts +0 -352
  72. package/src/commands/run.ts +0 -216
  73. package/src/commands/worktree-lib.ts +0 -279
  74. package/src/commands/worktree-merge-history-lib.ts +0 -764
  75. package/src/commands/worktree.ts +0 -2876
  76. package/src/config/data-dir.ts +0 -48
  77. package/src/config/env.ts +0 -125
  78. package/src/config/home.ts +0 -80
  79. package/src/config/hostnames.ts +0 -26
  80. package/src/config/schema.ts +0 -30
  81. package/src/config/secrets-key.ts +0 -48
  82. package/src/config/server-bind.ts +0 -183
  83. package/src/config/store.ts +0 -120
  84. package/src/index.ts +0 -182
  85. package/src/prompts/database.ts +0 -157
  86. package/src/prompts/llm.ts +0 -43
  87. package/src/prompts/logging.ts +0 -37
  88. package/src/prompts/secrets.ts +0 -99
  89. package/src/prompts/server.ts +0 -221
  90. package/src/prompts/storage.ts +0 -146
  91. package/src/telemetry.ts +0 -49
  92. package/src/utils/banner.ts +0 -24
  93. package/src/utils/net.ts +0 -18
  94. package/src/utils/path-resolver.ts +0 -25
  95. package/src/version.ts +0 -10
@@ -1,599 +0,0 @@
1
- import { describe, expect, it } from "vitest";
2
- import type { CompanyPortabilityPreviewResult } from "@paperclipai/shared";
3
- import {
4
- buildCompanyDashboardUrl,
5
- buildDefaultImportAdapterOverrides,
6
- buildDefaultImportSelectionState,
7
- buildImportSelectionCatalog,
8
- buildSelectedFilesFromImportSelection,
9
- renderCompanyImportPreview,
10
- renderCompanyImportResult,
11
- resolveCompanyImportApplyConfirmationMode,
12
- resolveCompanyImportApiPath,
13
- } from "../commands/client/company.js";
14
-
15
- describe("resolveCompanyImportApiPath", () => {
16
- it("uses company-scoped preview route for existing-company dry runs", () => {
17
- expect(
18
- resolveCompanyImportApiPath({
19
- dryRun: true,
20
- targetMode: "existing_company",
21
- companyId: "company-123",
22
- }),
23
- ).toBe("/api/companies/company-123/imports/preview");
24
- });
25
-
26
- it("uses company-scoped apply route for existing-company imports", () => {
27
- expect(
28
- resolveCompanyImportApiPath({
29
- dryRun: false,
30
- targetMode: "existing_company",
31
- companyId: "company-123",
32
- }),
33
- ).toBe("/api/companies/company-123/imports/apply");
34
- });
35
-
36
- it("keeps global routes for new-company imports", () => {
37
- expect(
38
- resolveCompanyImportApiPath({
39
- dryRun: true,
40
- targetMode: "new_company",
41
- }),
42
- ).toBe("/api/companies/import/preview");
43
-
44
- expect(
45
- resolveCompanyImportApiPath({
46
- dryRun: false,
47
- targetMode: "new_company",
48
- }),
49
- ).toBe("/api/companies/import");
50
- });
51
-
52
- it("throws when an existing-company import is missing a company id", () => {
53
- expect(() =>
54
- resolveCompanyImportApiPath({
55
- dryRun: true,
56
- targetMode: "existing_company",
57
- companyId: " ",
58
- })
59
- ).toThrow(/require a companyId/i);
60
- });
61
- });
62
-
63
- describe("resolveCompanyImportApplyConfirmationMode", () => {
64
- it("skips confirmation when --yes is set", () => {
65
- expect(
66
- resolveCompanyImportApplyConfirmationMode({
67
- yes: true,
68
- interactive: false,
69
- json: false,
70
- }),
71
- ).toBe("skip");
72
- });
73
-
74
- it("prompts in interactive text mode when --yes is not set", () => {
75
- expect(
76
- resolveCompanyImportApplyConfirmationMode({
77
- yes: false,
78
- interactive: true,
79
- json: false,
80
- }),
81
- ).toBe("prompt");
82
- });
83
-
84
- it("requires --yes for non-interactive apply", () => {
85
- expect(() =>
86
- resolveCompanyImportApplyConfirmationMode({
87
- yes: false,
88
- interactive: false,
89
- json: false,
90
- })
91
- ).toThrow(/non-interactive terminal requires --yes/i);
92
- });
93
-
94
- it("requires --yes for json apply", () => {
95
- expect(() =>
96
- resolveCompanyImportApplyConfirmationMode({
97
- yes: false,
98
- interactive: false,
99
- json: true,
100
- })
101
- ).toThrow(/with --json requires --yes/i);
102
- });
103
- });
104
-
105
- describe("buildCompanyDashboardUrl", () => {
106
- it("preserves the configured base path when building a dashboard URL", () => {
107
- expect(buildCompanyDashboardUrl("https://paperclip.example/app/", "PAP")).toBe(
108
- "https://paperclip.example/app/PAP/dashboard",
109
- );
110
- });
111
- });
112
-
113
- describe("renderCompanyImportPreview", () => {
114
- it("summarizes the preview with counts, selection info, and truncated examples", () => {
115
- const preview: CompanyPortabilityPreviewResult = {
116
- include: {
117
- company: true,
118
- agents: true,
119
- projects: true,
120
- issues: true,
121
- skills: true,
122
- },
123
- targetCompanyId: "company-123",
124
- targetCompanyName: "Imported Co",
125
- collisionStrategy: "rename",
126
- selectedAgentSlugs: ["ceo", "cto", "eng-1", "eng-2", "eng-3", "eng-4", "eng-5"],
127
- plan: {
128
- companyAction: "update",
129
- agentPlans: [
130
- { slug: "ceo", action: "create", plannedName: "CEO", existingAgentId: null, reason: null },
131
- { slug: "cto", action: "update", plannedName: "CTO", existingAgentId: "agent-2", reason: "replace strategy" },
132
- { slug: "eng-1", action: "skip", plannedName: "Engineer 1", existingAgentId: "agent-3", reason: "skip strategy" },
133
- { slug: "eng-2", action: "create", plannedName: "Engineer 2", existingAgentId: null, reason: null },
134
- { slug: "eng-3", action: "create", plannedName: "Engineer 3", existingAgentId: null, reason: null },
135
- { slug: "eng-4", action: "create", plannedName: "Engineer 4", existingAgentId: null, reason: null },
136
- { slug: "eng-5", action: "create", plannedName: "Engineer 5", existingAgentId: null, reason: null },
137
- ],
138
- projectPlans: [
139
- { slug: "alpha", action: "create", plannedName: "Alpha", existingProjectId: null, reason: null },
140
- ],
141
- issuePlans: [
142
- { slug: "kickoff", action: "create", plannedTitle: "Kickoff", reason: null },
143
- ],
144
- },
145
- manifest: {
146
- schemaVersion: 1,
147
- generatedAt: "2026-03-23T17:00:00.000Z",
148
- source: {
149
- companyId: "company-src",
150
- companyName: "Source Co",
151
- },
152
- includes: {
153
- company: true,
154
- agents: true,
155
- projects: true,
156
- issues: true,
157
- skills: true,
158
- },
159
- company: {
160
- path: "COMPANY.md",
161
- name: "Source Co",
162
- description: null,
163
- brandColor: null,
164
- logoPath: null,
165
- requireBoardApprovalForNewAgents: false,
166
- feedbackDataSharingEnabled: false,
167
- feedbackDataSharingConsentAt: null,
168
- feedbackDataSharingConsentByUserId: null,
169
- feedbackDataSharingTermsVersion: null,
170
- },
171
- sidebar: {
172
- agents: ["ceo"],
173
- projects: ["alpha"],
174
- },
175
- agents: [
176
- {
177
- slug: "ceo",
178
- name: "CEO",
179
- path: "agents/ceo/AGENT.md",
180
- skills: [],
181
- role: "ceo",
182
- title: null,
183
- icon: null,
184
- capabilities: null,
185
- reportsToSlug: null,
186
- adapterType: "codex_local",
187
- adapterConfig: {},
188
- runtimeConfig: {},
189
- permissions: {},
190
- budgetMonthlyCents: 0,
191
- metadata: null,
192
- },
193
- ],
194
- skills: [
195
- {
196
- key: "skill-a",
197
- slug: "skill-a",
198
- name: "Skill A",
199
- path: "skills/skill-a/SKILL.md",
200
- description: null,
201
- sourceType: "inline",
202
- sourceLocator: null,
203
- sourceRef: null,
204
- trustLevel: null,
205
- compatibility: null,
206
- metadata: null,
207
- fileInventory: [],
208
- },
209
- ],
210
- projects: [
211
- {
212
- slug: "alpha",
213
- name: "Alpha",
214
- path: "projects/alpha/PROJECT.md",
215
- description: null,
216
- ownerAgentSlug: null,
217
- leadAgentSlug: null,
218
- targetDate: null,
219
- color: null,
220
- status: null,
221
- executionWorkspacePolicy: null,
222
- workspaces: [],
223
- env: null,
224
- metadata: null,
225
- },
226
- ],
227
- issues: [
228
- {
229
- slug: "kickoff",
230
- identifier: null,
231
- title: "Kickoff",
232
- path: "projects/alpha/issues/kickoff/TASK.md",
233
- projectSlug: "alpha",
234
- projectWorkspaceKey: null,
235
- assigneeAgentSlug: "ceo",
236
- description: null,
237
- recurring: false,
238
- routine: null,
239
- legacyRecurrence: null,
240
- status: null,
241
- priority: null,
242
- labelIds: [],
243
- billingCode: null,
244
- executionWorkspaceSettings: null,
245
- assigneeAdapterOverrides: null,
246
- metadata: null,
247
- },
248
- ],
249
- envInputs: [
250
- {
251
- key: "OPENAI_API_KEY",
252
- description: null,
253
- agentSlug: "ceo",
254
- projectSlug: null,
255
- kind: "secret",
256
- requirement: "required",
257
- defaultValue: null,
258
- portability: "portable",
259
- },
260
- ],
261
- },
262
- files: {
263
- "COMPANY.md": "# Source Co",
264
- },
265
- envInputs: [
266
- {
267
- key: "OPENAI_API_KEY",
268
- description: null,
269
- agentSlug: "ceo",
270
- projectSlug: null,
271
- kind: "secret",
272
- requirement: "required",
273
- defaultValue: null,
274
- portability: "portable",
275
- },
276
- ],
277
- warnings: ["One warning"],
278
- errors: ["One error"],
279
- };
280
-
281
- const rendered = renderCompanyImportPreview(preview, {
282
- sourceLabel: "GitHub: https://github.com/paperclipai/companies/demo",
283
- targetLabel: "Imported Co (company-123)",
284
- infoMessages: ["Using claude-local adapter"],
285
- });
286
-
287
- expect(rendered).toContain("Include");
288
- expect(rendered).toContain("company, projects, tasks, agents, skills");
289
- expect(rendered).toContain("7 agents total");
290
- expect(rendered).toContain("1 project total");
291
- expect(rendered).toContain("1 task total");
292
- expect(rendered).toContain("skills: 1 skill packaged");
293
- expect(rendered).toContain("+1 more");
294
- expect(rendered).toContain("Using claude-local adapter");
295
- expect(rendered).toContain("Warnings");
296
- expect(rendered).toContain("Errors");
297
- });
298
- });
299
-
300
- describe("renderCompanyImportResult", () => {
301
- it("summarizes import results with created, updated, and skipped counts", () => {
302
- const rendered = renderCompanyImportResult(
303
- {
304
- company: {
305
- id: "company-123",
306
- name: "Imported Co",
307
- action: "updated",
308
- },
309
- agents: [
310
- { slug: "ceo", id: "agent-1", action: "created", name: "CEO", reason: null },
311
- { slug: "cto", id: "agent-2", action: "updated", name: "CTO", reason: "replace strategy" },
312
- { slug: "ops", id: null, action: "skipped", name: "Ops", reason: "skip strategy" },
313
- ],
314
- projects: [
315
- { slug: "app", id: "project-1", action: "created", name: "App", reason: null },
316
- { slug: "ops", id: "project-2", action: "updated", name: "Operations", reason: "replace strategy" },
317
- { slug: "archive", id: null, action: "skipped", name: "Archive", reason: "skip strategy" },
318
- ],
319
- envInputs: [],
320
- warnings: ["Review API keys"],
321
- },
322
- {
323
- targetLabel: "Imported Co (company-123)",
324
- companyUrl: "https://paperclip.example/PAP/dashboard",
325
- infoMessages: ["Using claude-local adapter"],
326
- },
327
- );
328
-
329
- expect(rendered).toContain("Company");
330
- expect(rendered).toContain("https://paperclip.example/PAP/dashboard");
331
- expect(rendered).toContain("3 agents total (1 created, 1 updated, 1 skipped)");
332
- expect(rendered).toContain("3 projects total (1 created, 1 updated, 1 skipped)");
333
- expect(rendered).toContain("Agent results");
334
- expect(rendered).toContain("Project results");
335
- expect(rendered).toContain("Using claude-local adapter");
336
- expect(rendered).toContain("Review API keys");
337
- });
338
- });
339
-
340
- describe("import selection catalog", () => {
341
- it("defaults to everything and keeps project selection separate from task selection", () => {
342
- const preview: CompanyPortabilityPreviewResult = {
343
- include: {
344
- company: true,
345
- agents: true,
346
- projects: true,
347
- issues: true,
348
- skills: true,
349
- },
350
- targetCompanyId: "company-123",
351
- targetCompanyName: "Imported Co",
352
- collisionStrategy: "rename",
353
- selectedAgentSlugs: ["ceo"],
354
- plan: {
355
- companyAction: "create",
356
- agentPlans: [],
357
- projectPlans: [],
358
- issuePlans: [],
359
- },
360
- manifest: {
361
- schemaVersion: 1,
362
- generatedAt: "2026-03-23T18:00:00.000Z",
363
- source: {
364
- companyId: "company-src",
365
- companyName: "Source Co",
366
- },
367
- includes: {
368
- company: true,
369
- agents: true,
370
- projects: true,
371
- issues: true,
372
- skills: true,
373
- },
374
- company: {
375
- path: "COMPANY.md",
376
- name: "Source Co",
377
- description: null,
378
- brandColor: null,
379
- logoPath: "images/company-logo.png",
380
- requireBoardApprovalForNewAgents: false,
381
- feedbackDataSharingEnabled: false,
382
- feedbackDataSharingConsentAt: null,
383
- feedbackDataSharingConsentByUserId: null,
384
- feedbackDataSharingTermsVersion: null,
385
- },
386
- sidebar: {
387
- agents: ["ceo"],
388
- projects: ["alpha"],
389
- },
390
- agents: [
391
- {
392
- slug: "ceo",
393
- name: "CEO",
394
- path: "agents/ceo/AGENT.md",
395
- skills: [],
396
- role: "ceo",
397
- title: null,
398
- icon: null,
399
- capabilities: null,
400
- reportsToSlug: null,
401
- adapterType: "codex_local",
402
- adapterConfig: {},
403
- runtimeConfig: {},
404
- permissions: {},
405
- budgetMonthlyCents: 0,
406
- metadata: null,
407
- },
408
- ],
409
- skills: [
410
- {
411
- key: "skill-a",
412
- slug: "skill-a",
413
- name: "Skill A",
414
- path: "skills/skill-a/SKILL.md",
415
- description: null,
416
- sourceType: "inline",
417
- sourceLocator: null,
418
- sourceRef: null,
419
- trustLevel: null,
420
- compatibility: null,
421
- metadata: null,
422
- fileInventory: [{ path: "skills/skill-a/helper.md", kind: "doc" }],
423
- },
424
- ],
425
- projects: [
426
- {
427
- slug: "alpha",
428
- name: "Alpha",
429
- path: "projects/alpha/PROJECT.md",
430
- description: null,
431
- ownerAgentSlug: null,
432
- leadAgentSlug: null,
433
- targetDate: null,
434
- color: null,
435
- status: null,
436
- executionWorkspacePolicy: null,
437
- workspaces: [],
438
- env: null,
439
- metadata: null,
440
- },
441
- ],
442
- issues: [
443
- {
444
- slug: "kickoff",
445
- identifier: null,
446
- title: "Kickoff",
447
- path: "projects/alpha/issues/kickoff/TASK.md",
448
- projectSlug: "alpha",
449
- projectWorkspaceKey: null,
450
- assigneeAgentSlug: "ceo",
451
- description: null,
452
- recurring: false,
453
- routine: null,
454
- legacyRecurrence: null,
455
- status: null,
456
- priority: null,
457
- labelIds: [],
458
- billingCode: null,
459
- executionWorkspaceSettings: null,
460
- assigneeAdapterOverrides: null,
461
- metadata: null,
462
- },
463
- ],
464
- envInputs: [],
465
- },
466
- files: {
467
- "COMPANY.md": "# Source Co",
468
- "README.md": "# Readme",
469
- ".paperclip.yaml": "schema: paperclip/v1\n",
470
- "images/company-logo.png": {
471
- encoding: "base64",
472
- data: "",
473
- contentType: "image/png",
474
- },
475
- "projects/alpha/PROJECT.md": "# Alpha",
476
- "projects/alpha/notes.md": "project notes",
477
- "projects/alpha/issues/kickoff/TASK.md": "# Kickoff",
478
- "projects/alpha/issues/kickoff/details.md": "task details",
479
- "agents/ceo/AGENT.md": "# CEO",
480
- "agents/ceo/prompt.md": "prompt",
481
- "skills/skill-a/SKILL.md": "# Skill A",
482
- "skills/skill-a/helper.md": "helper",
483
- },
484
- envInputs: [],
485
- warnings: [],
486
- errors: [],
487
- };
488
-
489
- const catalog = buildImportSelectionCatalog(preview);
490
- const state = buildDefaultImportSelectionState(catalog);
491
-
492
- expect(state.company).toBe(true);
493
- expect(state.projects.has("alpha")).toBe(true);
494
- expect(state.issues.has("kickoff")).toBe(true);
495
- expect(state.agents.has("ceo")).toBe(true);
496
- expect(state.skills.has("skill-a")).toBe(true);
497
-
498
- state.company = false;
499
- state.issues.clear();
500
- state.agents.clear();
501
- state.skills.clear();
502
-
503
- const selectedFiles = buildSelectedFilesFromImportSelection(catalog, state);
504
-
505
- expect(selectedFiles).toContain(".paperclip.yaml");
506
- expect(selectedFiles).toContain("projects/alpha/PROJECT.md");
507
- expect(selectedFiles).toContain("projects/alpha/notes.md");
508
- expect(selectedFiles).not.toContain("projects/alpha/issues/kickoff/TASK.md");
509
- expect(selectedFiles).not.toContain("projects/alpha/issues/kickoff/details.md");
510
- });
511
- });
512
-
513
- describe("default adapter overrides", () => {
514
- it("maps process-only imported agents to claude_local", () => {
515
- const preview: CompanyPortabilityPreviewResult = {
516
- include: {
517
- company: false,
518
- agents: true,
519
- projects: false,
520
- issues: false,
521
- skills: false,
522
- },
523
- targetCompanyId: null,
524
- targetCompanyName: null,
525
- collisionStrategy: "rename",
526
- selectedAgentSlugs: ["legacy-agent", "explicit-agent"],
527
- plan: {
528
- companyAction: "none",
529
- agentPlans: [],
530
- projectPlans: [],
531
- issuePlans: [],
532
- },
533
- manifest: {
534
- schemaVersion: 1,
535
- generatedAt: "2026-03-23T18:20:00.000Z",
536
- source: null,
537
- includes: {
538
- company: false,
539
- agents: true,
540
- projects: false,
541
- issues: false,
542
- skills: false,
543
- },
544
- company: null,
545
- sidebar: null,
546
- agents: [
547
- {
548
- slug: "legacy-agent",
549
- name: "Legacy Agent",
550
- path: "agents/legacy-agent/AGENT.md",
551
- skills: [],
552
- role: "agent",
553
- title: null,
554
- icon: null,
555
- capabilities: null,
556
- reportsToSlug: null,
557
- adapterType: "process",
558
- adapterConfig: {},
559
- runtimeConfig: {},
560
- permissions: {},
561
- budgetMonthlyCents: 0,
562
- metadata: null,
563
- },
564
- {
565
- slug: "explicit-agent",
566
- name: "Explicit Agent",
567
- path: "agents/explicit-agent/AGENT.md",
568
- skills: [],
569
- role: "agent",
570
- title: null,
571
- icon: null,
572
- capabilities: null,
573
- reportsToSlug: null,
574
- adapterType: "codex_local",
575
- adapterConfig: {},
576
- runtimeConfig: {},
577
- permissions: {},
578
- budgetMonthlyCents: 0,
579
- metadata: null,
580
- },
581
- ],
582
- skills: [],
583
- projects: [],
584
- issues: [],
585
- envInputs: [],
586
- },
587
- files: {},
588
- envInputs: [],
589
- warnings: [],
590
- errors: [],
591
- };
592
-
593
- expect(buildDefaultImportAdapterOverrides(preview)).toEqual({
594
- "legacy-agent": {
595
- adapterType: "claude_local",
596
- },
597
- });
598
- });
599
- });
@@ -1,70 +0,0 @@
1
- import fs from "node:fs";
2
- import os from "node:os";
3
- import path from "node:path";
4
- import { describe, expect, it } from "vitest";
5
- import {
6
- defaultClientContext,
7
- readContext,
8
- setCurrentProfile,
9
- upsertProfile,
10
- writeContext,
11
- } from "../client/context.js";
12
-
13
- function createTempContextPath(): string {
14
- const dir = fs.mkdtempSync(path.join(os.tmpdir(), "paperclip-cli-context-"));
15
- return path.join(dir, "context.json");
16
- }
17
-
18
- describe("client context store", () => {
19
- it("returns default context when file does not exist", () => {
20
- const contextPath = createTempContextPath();
21
- const context = readContext(contextPath);
22
- expect(context).toEqual(defaultClientContext());
23
- });
24
-
25
- it("upserts profile values and switches current profile", () => {
26
- const contextPath = createTempContextPath();
27
-
28
- upsertProfile(
29
- "work",
30
- {
31
- apiBase: "http://localhost:3100",
32
- companyId: "company-123",
33
- apiKeyEnvVarName: "PAPERCLIP_AGENT_TOKEN",
34
- },
35
- contextPath,
36
- );
37
-
38
- setCurrentProfile("work", contextPath);
39
- const context = readContext(contextPath);
40
-
41
- expect(context.currentProfile).toBe("work");
42
- expect(context.profiles.work).toEqual({
43
- apiBase: "http://localhost:3100",
44
- companyId: "company-123",
45
- apiKeyEnvVarName: "PAPERCLIP_AGENT_TOKEN",
46
- });
47
- });
48
-
49
- it("normalizes invalid file content to safe defaults", () => {
50
- const contextPath = createTempContextPath();
51
- writeContext(
52
- {
53
- version: 1,
54
- currentProfile: "x",
55
- profiles: {
56
- x: {
57
- apiBase: " ",
58
- companyId: " ",
59
- apiKeyEnvVarName: " ",
60
- },
61
- },
62
- },
63
- contextPath,
64
- );
65
-
66
- const context = readContext(contextPath);
67
- expect(context.currentProfile).toBe("x");
68
- expect(context.profiles.x).toEqual({});
69
- });
70
- });