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,279 +0,0 @@
1
- import { randomInt } from "node:crypto";
2
- import path from "node:path";
3
- import type { PaperclipConfig } from "../config/schema.js";
4
- import { expandHomePrefix } from "../config/home.js";
5
-
6
- export const DEFAULT_WORKTREE_HOME = "~/.paperclip-worktrees";
7
- export const WORKTREE_SEED_MODES = ["minimal", "full"] as const;
8
-
9
- export type WorktreeSeedMode = (typeof WORKTREE_SEED_MODES)[number];
10
-
11
- export type WorktreeSeedPlan = {
12
- mode: WorktreeSeedMode;
13
- excludedTables: string[];
14
- nullifyColumns: Record<string, string[]>;
15
- };
16
-
17
- const MINIMAL_WORKTREE_EXCLUDED_TABLES = [
18
- "activity_log",
19
- "agent_runtime_state",
20
- "agent_task_sessions",
21
- "agent_wakeup_requests",
22
- "cost_events",
23
- "heartbeat_run_events",
24
- "heartbeat_runs",
25
- "workspace_runtime_services",
26
- ];
27
-
28
- const MINIMAL_WORKTREE_NULLIFIED_COLUMNS: Record<string, string[]> = {
29
- issues: ["checkout_run_id", "execution_run_id"],
30
- };
31
-
32
- export type WorktreeLocalPaths = {
33
- cwd: string;
34
- repoConfigDir: string;
35
- configPath: string;
36
- envPath: string;
37
- homeDir: string;
38
- instanceId: string;
39
- instanceRoot: string;
40
- contextPath: string;
41
- embeddedPostgresDataDir: string;
42
- backupDir: string;
43
- logDir: string;
44
- secretsKeyFilePath: string;
45
- storageDir: string;
46
- };
47
-
48
- export type WorktreeUiBranding = {
49
- name: string;
50
- color: string;
51
- };
52
-
53
- export function isWorktreeSeedMode(value: string): value is WorktreeSeedMode {
54
- return (WORKTREE_SEED_MODES as readonly string[]).includes(value);
55
- }
56
-
57
- export function resolveWorktreeSeedPlan(mode: WorktreeSeedMode): WorktreeSeedPlan {
58
- if (mode === "full") {
59
- return {
60
- mode,
61
- excludedTables: [],
62
- nullifyColumns: {},
63
- };
64
- }
65
- return {
66
- mode,
67
- excludedTables: [...MINIMAL_WORKTREE_EXCLUDED_TABLES],
68
- nullifyColumns: {
69
- ...MINIMAL_WORKTREE_NULLIFIED_COLUMNS,
70
- },
71
- };
72
- }
73
-
74
- function nonEmpty(value: string | null | undefined): string | null {
75
- return typeof value === "string" && value.trim().length > 0 ? value.trim() : null;
76
- }
77
-
78
- function isLoopbackHost(hostname: string): boolean {
79
- const value = hostname.trim().toLowerCase();
80
- return value === "127.0.0.1" || value === "localhost" || value === "::1";
81
- }
82
-
83
- export function sanitizeWorktreeInstanceId(rawValue: string): string {
84
- const trimmed = rawValue.trim().toLowerCase();
85
- const normalized = trimmed
86
- .replace(/[^a-z0-9_-]+/g, "-")
87
- .replace(/-+/g, "-")
88
- .replace(/^[-_]+|[-_]+$/g, "");
89
- return normalized || "worktree";
90
- }
91
-
92
- export function resolveSuggestedWorktreeName(cwd: string, explicitName?: string): string {
93
- return nonEmpty(explicitName) ?? path.basename(path.resolve(cwd));
94
- }
95
-
96
- function hslComponentToHex(n: number): string {
97
- return Math.round(Math.max(0, Math.min(255, n)))
98
- .toString(16)
99
- .padStart(2, "0");
100
- }
101
-
102
- function hslToHex(hue: number, saturation: number, lightness: number): string {
103
- const s = Math.max(0, Math.min(100, saturation)) / 100;
104
- const l = Math.max(0, Math.min(100, lightness)) / 100;
105
- const c = (1 - Math.abs((2 * l) - 1)) * s;
106
- const h = ((hue % 360) + 360) % 360;
107
- const x = c * (1 - Math.abs(((h / 60) % 2) - 1));
108
- const m = l - (c / 2);
109
-
110
- let r = 0;
111
- let g = 0;
112
- let b = 0;
113
-
114
- if (h < 60) {
115
- r = c;
116
- g = x;
117
- } else if (h < 120) {
118
- r = x;
119
- g = c;
120
- } else if (h < 180) {
121
- g = c;
122
- b = x;
123
- } else if (h < 240) {
124
- g = x;
125
- b = c;
126
- } else if (h < 300) {
127
- r = x;
128
- b = c;
129
- } else {
130
- r = c;
131
- b = x;
132
- }
133
-
134
- return `#${hslComponentToHex((r + m) * 255)}${hslComponentToHex((g + m) * 255)}${hslComponentToHex((b + m) * 255)}`;
135
- }
136
-
137
- export function generateWorktreeColor(): string {
138
- return hslToHex(randomInt(0, 360), 68, 56);
139
- }
140
-
141
- export function resolveWorktreeLocalPaths(opts: {
142
- cwd: string;
143
- homeDir?: string;
144
- instanceId: string;
145
- }): WorktreeLocalPaths {
146
- const cwd = path.resolve(opts.cwd);
147
- const homeDir = path.resolve(expandHomePrefix(opts.homeDir ?? DEFAULT_WORKTREE_HOME));
148
- const instanceRoot = path.resolve(homeDir, "instances", opts.instanceId);
149
- const repoConfigDir = path.resolve(cwd, ".paperclip");
150
- return {
151
- cwd,
152
- repoConfigDir,
153
- configPath: path.resolve(repoConfigDir, "config.json"),
154
- envPath: path.resolve(repoConfigDir, ".env"),
155
- homeDir,
156
- instanceId: opts.instanceId,
157
- instanceRoot,
158
- contextPath: path.resolve(homeDir, "context.json"),
159
- embeddedPostgresDataDir: path.resolve(instanceRoot, "db"),
160
- backupDir: path.resolve(instanceRoot, "data", "backups"),
161
- logDir: path.resolve(instanceRoot, "logs"),
162
- secretsKeyFilePath: path.resolve(instanceRoot, "secrets", "master.key"),
163
- storageDir: path.resolve(instanceRoot, "data", "storage"),
164
- };
165
- }
166
-
167
- export function rewriteLocalUrlPort(rawUrl: string | undefined, port: number): string | undefined {
168
- if (!rawUrl) return undefined;
169
- try {
170
- const parsed = new URL(rawUrl);
171
- if (!isLoopbackHost(parsed.hostname)) return rawUrl;
172
- parsed.port = String(port);
173
- return parsed.toString();
174
- } catch {
175
- return rawUrl;
176
- }
177
- }
178
-
179
- export function buildWorktreeConfig(input: {
180
- sourceConfig: PaperclipConfig | null;
181
- paths: WorktreeLocalPaths;
182
- serverPort: number;
183
- databasePort: number;
184
- now?: Date;
185
- }): PaperclipConfig {
186
- const { sourceConfig, paths, serverPort, databasePort } = input;
187
- const nowIso = (input.now ?? new Date()).toISOString();
188
-
189
- const source = sourceConfig;
190
- const authPublicBaseUrl = rewriteLocalUrlPort(source?.auth.publicBaseUrl, serverPort);
191
-
192
- return {
193
- $meta: {
194
- version: 1,
195
- updatedAt: nowIso,
196
- source: "configure",
197
- },
198
- ...(source?.llm ? { llm: source.llm } : {}),
199
- database: {
200
- mode: "embedded-postgres",
201
- embeddedPostgresDataDir: paths.embeddedPostgresDataDir,
202
- embeddedPostgresPort: databasePort,
203
- backup: {
204
- enabled: source?.database.backup.enabled ?? true,
205
- intervalMinutes: source?.database.backup.intervalMinutes ?? 60,
206
- retentionDays: source?.database.backup.retentionDays ?? 30,
207
- dir: paths.backupDir,
208
- },
209
- },
210
- logging: {
211
- mode: source?.logging.mode ?? "file",
212
- logDir: paths.logDir,
213
- },
214
- server: {
215
- deploymentMode: source?.server.deploymentMode ?? "local_trusted",
216
- exposure: source?.server.exposure ?? "private",
217
- ...(source?.server.bind ? { bind: source.server.bind } : {}),
218
- ...(source?.server.customBindHost ? { customBindHost: source.server.customBindHost } : {}),
219
- host: source?.server.host ?? "127.0.0.1",
220
- port: serverPort,
221
- allowedHostnames: source?.server.allowedHostnames ?? [],
222
- serveUi: source?.server.serveUi ?? true,
223
- },
224
- auth: {
225
- baseUrlMode: source?.auth.baseUrlMode ?? "auto",
226
- ...(authPublicBaseUrl ? { publicBaseUrl: authPublicBaseUrl } : {}),
227
- disableSignUp: source?.auth.disableSignUp ?? false,
228
- },
229
- telemetry: {
230
- enabled: source?.telemetry?.enabled ?? true,
231
- },
232
- storage: {
233
- provider: source?.storage.provider ?? "local_disk",
234
- localDisk: {
235
- baseDir: paths.storageDir,
236
- },
237
- s3: {
238
- bucket: source?.storage.s3.bucket ?? "paperclip",
239
- region: source?.storage.s3.region ?? "us-east-1",
240
- endpoint: source?.storage.s3.endpoint,
241
- prefix: source?.storage.s3.prefix ?? "",
242
- forcePathStyle: source?.storage.s3.forcePathStyle ?? false,
243
- },
244
- },
245
- secrets: {
246
- provider: source?.secrets.provider ?? "local_encrypted",
247
- strictMode: source?.secrets.strictMode ?? false,
248
- localEncrypted: {
249
- keyFilePath: paths.secretsKeyFilePath,
250
- },
251
- },
252
- };
253
- }
254
-
255
- export function buildWorktreeEnvEntries(
256
- paths: WorktreeLocalPaths,
257
- branding?: WorktreeUiBranding,
258
- ): Record<string, string> {
259
- return {
260
- PAPERCLIP_HOME: paths.homeDir,
261
- PAPERCLIP_INSTANCE_ID: paths.instanceId,
262
- PAPERCLIP_CONFIG: paths.configPath,
263
- PAPERCLIP_CONTEXT: paths.contextPath,
264
- PAPERCLIP_IN_WORKTREE: "true",
265
- ...(branding?.name ? { PAPERCLIP_WORKTREE_NAME: branding.name } : {}),
266
- ...(branding?.color ? { PAPERCLIP_WORKTREE_COLOR: branding.color } : {}),
267
- };
268
- }
269
-
270
- function shellEscape(value: string): string {
271
- return `'${value.replaceAll("'", `'\"'\"'`)}'`;
272
- }
273
-
274
- export function formatShellExports(entries: Record<string, string>): string {
275
- return Object.entries(entries)
276
- .filter(([, value]) => typeof value === "string" && value.trim().length > 0)
277
- .map(([key, value]) => `export ${key}=${shellEscape(value)}`)
278
- .join("\n");
279
- }