@ctrl-spc/cs 0.1.0 → 0.2.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.
- package/dist/codebases.js +108 -0
- package/dist/companion-ui.js +856 -0
- package/dist/companion.js +374 -0
- package/dist/config.js +169 -10
- package/dist/daemon.js +12 -79
- package/dist/env.js +5 -0
- package/dist/folders.js +67 -0
- package/dist/git-remote.js +79 -0
- package/dist/index.js +7 -18
- package/dist/presence.js +144 -0
- package/dist/projects.js +32 -0
- package/dist/supabase.js +21 -1
- package/package.json +1 -1
|
@@ -0,0 +1,108 @@
|
|
|
1
|
+
import { hostedRemoteIdentity } from './git-remote.js';
|
|
2
|
+
/** Raised when a folder's git remote isn't a real hosted repository URL, so it
|
|
3
|
+
* can't become a codebase. The route surfaces it as a 400 refusal. */
|
|
4
|
+
export class NotHostedRemoteError extends Error {
|
|
5
|
+
constructor(message = "This folder's git remote isn't a hosted repository URL, so it can't become a codebase.") {
|
|
6
|
+
super(message);
|
|
7
|
+
this.name = 'NotHostedRemoteError';
|
|
8
|
+
}
|
|
9
|
+
}
|
|
10
|
+
/**
|
|
11
|
+
* The codebases registered on one project.
|
|
12
|
+
*
|
|
13
|
+
* Rows live in the isolated `cliv2_codebases` table (org-scoped RLS mirroring
|
|
14
|
+
* `public.projects`, so this returns every codebase on the project — a codebase
|
|
15
|
+
* is shared project state visible to every member of the project's org, not a
|
|
16
|
+
* per-user list). `project_id` references a shared `projects` row by value with
|
|
17
|
+
* no FK — the v2 surface stays decoupled from v1-owned schema. Only path-free
|
|
18
|
+
* identities (canonical remote + derived name) are stored, never an absolute
|
|
19
|
+
* local path (each user's local folder path stays in the CLI's local config).
|
|
20
|
+
*/
|
|
21
|
+
export async function listCodebases(client, projectId) {
|
|
22
|
+
const { data, error } = await client
|
|
23
|
+
.from('cliv2_codebases')
|
|
24
|
+
.select('id, git_remote_url, name')
|
|
25
|
+
.eq('project_id', projectId)
|
|
26
|
+
.order('created_at', { ascending: true });
|
|
27
|
+
if (error)
|
|
28
|
+
throw new Error(error.message);
|
|
29
|
+
return (data ?? []).map((row) => ({
|
|
30
|
+
id: row.id,
|
|
31
|
+
gitRemoteUrl: row.git_remote_url,
|
|
32
|
+
name: row.name,
|
|
33
|
+
}));
|
|
34
|
+
}
|
|
35
|
+
/**
|
|
36
|
+
* A display name from a canonical `host/path` remote: its last `/` segment.
|
|
37
|
+
* E.g. `github.com/acme/web` → `web`. Throws rather than ever returning an
|
|
38
|
+
* empty name (the table's `name` column is `not null`, non-blank).
|
|
39
|
+
*/
|
|
40
|
+
export function deriveCodebaseName(identity) {
|
|
41
|
+
const name = identity.split('/').pop()?.trim() ?? '';
|
|
42
|
+
if (!name)
|
|
43
|
+
throw new Error(`Could not derive a codebase name from "${identity}".`);
|
|
44
|
+
return name;
|
|
45
|
+
}
|
|
46
|
+
/**
|
|
47
|
+
* Add a codebase to a project by its git remote URL. The raw remote is
|
|
48
|
+
* canonicalized to `host/path` (stripping scheme, credentials, `.git`, trailing
|
|
49
|
+
* slash) BEFORE insert, so only a path-free, shareable identity reaches the
|
|
50
|
+
* cloud and the unique dedupe key actually holds ("same remote = same
|
|
51
|
+
* codebase"). A remote that isn't a real hosted repo is refused
|
|
52
|
+
* (NotHostedRemoteError) and never inserted. `added_by` defaults to auth.uid().
|
|
53
|
+
*
|
|
54
|
+
* Returns `{ added: false }` on a unique-violation — the dedupe key is now
|
|
55
|
+
* (project_id, git_remote_url), so this fires when ANY org member has already
|
|
56
|
+
* added this remote to the project — a no-op dedupe, never surfaced as an error.
|
|
57
|
+
*/
|
|
58
|
+
export async function addCodebase(client, projectId, gitRemoteUrl) {
|
|
59
|
+
const identity = hostedRemoteIdentity(gitRemoteUrl);
|
|
60
|
+
if (!identity)
|
|
61
|
+
throw new NotHostedRemoteError();
|
|
62
|
+
const name = deriveCodebaseName(identity);
|
|
63
|
+
const { error } = await client
|
|
64
|
+
.from('cliv2_codebases')
|
|
65
|
+
.insert({ project_id: projectId, git_remote_url: identity, name });
|
|
66
|
+
if (error) {
|
|
67
|
+
if (error.code === '23505')
|
|
68
|
+
return { added: false }; // dedupe: already on the project
|
|
69
|
+
throw new Error(error.message);
|
|
70
|
+
}
|
|
71
|
+
return { added: true };
|
|
72
|
+
}
|
|
73
|
+
/**
|
|
74
|
+
* Remove a codebase from its project — Phase 3. Deletes the ORG-SHARED
|
|
75
|
+
* `cliv2_codebases` row by id, so the codebase disappears for every member of
|
|
76
|
+
* the project's org (its RLS `using` = org membership, so any member may delete;
|
|
77
|
+
* no extra scoping needed here).
|
|
78
|
+
*
|
|
79
|
+
* Deliberately narrow: this deletes ONLY the codebase row. It does NOT touch
|
|
80
|
+
* per-machine availability (`cliv2_codebase_locations`) or the local folder
|
|
81
|
+
* mapping (`codebase-paths.json`) — those are per-repo truth, kept so a later
|
|
82
|
+
* re-add of the same remote shows this computer located again for free.
|
|
83
|
+
*/
|
|
84
|
+
export async function removeCodebase(client, codebaseId) {
|
|
85
|
+
const { error } = await client.from('cliv2_codebases').delete().eq('id', codebaseId);
|
|
86
|
+
if (error)
|
|
87
|
+
throw new Error(error.message);
|
|
88
|
+
}
|
|
89
|
+
/**
|
|
90
|
+
* Record that THIS machine has a codebase's folder checked out — Phase 2
|
|
91
|
+
* per-machine availability. Upserts one path-free row into the owner-scoped
|
|
92
|
+
* `cliv2_codebase_locations` table on `(user_id, machine_id, git_remote_url)`,
|
|
93
|
+
* refreshing `updated_at`. `user_id` defaults to `auth.uid()` server-side — the
|
|
94
|
+
* client never sets it — and RLS confines the row to the signed-in user's own
|
|
95
|
+
* machines (a user's availability is private, mirroring `cliv2_agents`).
|
|
96
|
+
*
|
|
97
|
+
* `identity` MUST already be a canonical `host/path` remote (the caller
|
|
98
|
+
* re-derives it from the folder via `hostedRemoteIdentity`). An absolute local
|
|
99
|
+
* path is NEVER sent — only the identity + this machine's id, honoring the
|
|
100
|
+
* product's path-privacy invariant.
|
|
101
|
+
*/
|
|
102
|
+
export async function reportLocated(client, machineId, identity) {
|
|
103
|
+
const { error } = await client
|
|
104
|
+
.from('cliv2_codebase_locations')
|
|
105
|
+
.upsert({ machine_id: machineId, git_remote_url: identity, updated_at: new Date().toISOString() }, { onConflict: 'user_id,machine_id,git_remote_url' });
|
|
106
|
+
if (error)
|
|
107
|
+
throw new Error(error.message);
|
|
108
|
+
}
|