@ctrl-spc/cli 1.1.15 → 1.2.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.
- package/dist/agents.js +54 -0
- package/dist/collision.js +44 -0
- package/dist/commands/fetch.js +213 -0
- package/dist/commands/init.js +457 -0
- package/dist/commands/link.js +152 -0
- package/dist/commands/login.js +332 -0
- package/dist/commands/logout.js +14 -0
- package/dist/commands/run.js +622 -0
- package/dist/config.js +68 -0
- package/dist/env.js +5 -0
- package/dist/git.js +70 -0
- package/dist/index.js +56 -72
- package/dist/mcp.js +1732 -0
- package/dist/presence.js +62 -0
- package/dist/prompt.js +47 -0
- package/dist/protocol.js +136 -0
- package/dist/skills.js +152 -0
- package/dist/supabase.js +40 -10
- package/dist/topology.js +602 -0
- package/package.json +25 -23
- package/bin/ctrl-spc.js +0 -6
- package/dist/auth.js +0 -172
- package/dist/controlRequests.js +0 -164
- package/dist/daemon.js +0 -152
- package/dist/devices.js +0 -64
- package/dist/flagAssembler.js +0 -29
- package/dist/init.js +0 -79
- package/dist/launchd.js +0 -154
- package/dist/lifecycle.js +0 -37
- package/dist/mcpConfig.js +0 -12
- package/dist/repo.js +0 -52
- package/dist/session.js +0 -58
- package/dist/setup.js +0 -60
- package/dist/spawner.js +0 -140
- package/dist/windows-service.js +0 -142
- package/node_modules/@ctrl-spc/mcp-server/dist/claimResolver.d.ts +0 -24
- package/node_modules/@ctrl-spc/mcp-server/dist/claimResolver.js +0 -32
- package/node_modules/@ctrl-spc/mcp-server/dist/fingerprint.d.ts +0 -2
- package/node_modules/@ctrl-spc/mcp-server/dist/fingerprint.js +0 -21
- package/node_modules/@ctrl-spc/mcp-server/dist/index.d.ts +0 -1
- package/node_modules/@ctrl-spc/mcp-server/dist/index.js +0 -22
- package/node_modules/@ctrl-spc/mcp-server/package.json +0 -38
package/dist/env.js
ADDED
|
@@ -0,0 +1,5 @@
|
|
|
1
|
+
/** Baked-in Supabase project defaults, overridable per Global Constraints. */
|
|
2
|
+
export const SUPABASE_URL = process.env.CTRL_SPC_SUPABASE_URL || 'https://gdioapkjgnlddsutysne.supabase.co';
|
|
3
|
+
export const SUPABASE_KEY = process.env.CTRL_SPC_SUPABASE_KEY || 'sb_publishable_izf3buHACC2LJKm2JH96YQ_safmL1k0';
|
|
4
|
+
/** MCP server port (Task 4 binds here); override via `CTRL_SPC_PORT`. */
|
|
5
|
+
export const MCP_PORT = Number(process.env.CTRL_SPC_PORT) || 4571;
|
package/dist/git.js
ADDED
|
@@ -0,0 +1,70 @@
|
|
|
1
|
+
import { execFileSync } from 'node:child_process';
|
|
2
|
+
/**
|
|
3
|
+
* Normalizes a git remote URL to a canonical `host/path` key (no scheme,
|
|
4
|
+
* lowercase throughout — hosts are case-insensitive and GitHub/GitLab paths
|
|
5
|
+
* redirect case-insensitively, so one repo should always map to one key).
|
|
6
|
+
* Non-default ports are kept (`host:port/path`); 22/443/80 are stripped.
|
|
7
|
+
*
|
|
8
|
+
* Verbatim from the task brief (spec §11 open item):
|
|
9
|
+
* git@github.com:User/Repo.git -> github.com/user/repo
|
|
10
|
+
* https://github.com/User/Repo/ -> github.com/user/repo
|
|
11
|
+
* ssh://git@Host.com:2222/x/y.git -> host.com:2222/x/y
|
|
12
|
+
* https://user:pass@host/x/y.git -> host/x/y
|
|
13
|
+
*/
|
|
14
|
+
export function normalizeRemoteUrl(raw) {
|
|
15
|
+
let s = raw.trim();
|
|
16
|
+
const scp = /^(?:[^@/]+@)([^:/]+):(?!\/\/)(.+)$/.exec(s); // scp-like git@host:path
|
|
17
|
+
if (scp)
|
|
18
|
+
s = `${scp[1]}/${scp[2]}`;
|
|
19
|
+
else {
|
|
20
|
+
s = s.replace(/^[a-z+]+:\/\//i, ''); // scheme
|
|
21
|
+
s = s.replace(/^[^@/]+(?::[^@/]*)?@/, ''); // user[:pass]@
|
|
22
|
+
}
|
|
23
|
+
s = s.replace(/\/+$/, '').replace(/\.git$/i, '');
|
|
24
|
+
s = s.replace(/^([^/:]+):(22|443|80)(?=\/)/, '$1'); // default ports
|
|
25
|
+
return s.toLowerCase();
|
|
26
|
+
}
|
|
27
|
+
/**
|
|
28
|
+
* Runs `git remote get-url origin` in `cwd`. Returns `null` if there's no
|
|
29
|
+
* `origin` remote, `cwd` isn't a git repository, or `git` isn't on PATH —
|
|
30
|
+
* callers treat all of those the same way ("no remote here").
|
|
31
|
+
*/
|
|
32
|
+
export function currentRepoRemote(cwd) {
|
|
33
|
+
try {
|
|
34
|
+
const out = execFileSync('git', ['remote', 'get-url', 'origin'], {
|
|
35
|
+
cwd,
|
|
36
|
+
encoding: 'utf8',
|
|
37
|
+
stdio: ['ignore', 'pipe', 'ignore'],
|
|
38
|
+
});
|
|
39
|
+
const trimmed = out.trim();
|
|
40
|
+
return trimmed || null;
|
|
41
|
+
}
|
|
42
|
+
catch {
|
|
43
|
+
return null;
|
|
44
|
+
}
|
|
45
|
+
}
|
|
46
|
+
/**
|
|
47
|
+
* Looks up the project registered for the current repo's git remote, scoped
|
|
48
|
+
* to the signed-in user's orgs by RLS (`projects_select` only surfaces rows
|
|
49
|
+
* in orgs the user is a member of). Returns `null` when there's no remote
|
|
50
|
+
* here, or no project the user can see is keyed on it. Throws on a genuine
|
|
51
|
+
* query/network error — that's distinct from "not registered" and callers
|
|
52
|
+
* (`init`/`link`) must not treat it as such.
|
|
53
|
+
*/
|
|
54
|
+
export async function resolveProject(client, cwd) {
|
|
55
|
+
const remote = currentRepoRemote(cwd);
|
|
56
|
+
if (!remote)
|
|
57
|
+
return null;
|
|
58
|
+
const key = normalizeRemoteUrl(remote);
|
|
59
|
+
const { data, error } = await client
|
|
60
|
+
.from('projects')
|
|
61
|
+
.select('id, org_id, name, organizations(name)')
|
|
62
|
+
.eq('git_remote_url', key)
|
|
63
|
+
.maybeSingle();
|
|
64
|
+
if (error)
|
|
65
|
+
throw new Error(`Failed to look up project for ${key}: ${error.message}`);
|
|
66
|
+
if (!data)
|
|
67
|
+
return null;
|
|
68
|
+
const row = data;
|
|
69
|
+
return { id: row.id, orgId: row.org_id, name: row.name, orgName: row.organizations?.name ?? '' };
|
|
70
|
+
}
|
package/dist/index.js
CHANGED
|
@@ -1,74 +1,58 @@
|
|
|
1
|
-
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
import { login } from './commands/login.js';
|
|
3
|
+
import { logout } from './commands/logout.js';
|
|
4
|
+
import { init } from './commands/init.js';
|
|
5
|
+
import { link } from './commands/link.js';
|
|
6
|
+
import { fetchRepository } from './commands/fetch.js';
|
|
7
|
+
import { run } from './commands/run.js';
|
|
8
|
+
export const HELP_TEXT = `ctrl-spc — CTRL+SPC CLI
|
|
9
|
+
|
|
10
|
+
Usage:
|
|
11
|
+
ctrl-spc login Sign in via the browser and store a session
|
|
12
|
+
ctrl-spc logout Remove the stored session from this machine
|
|
13
|
+
ctrl-spc init Discover repositories and explicitly group them into projects
|
|
14
|
+
ctrl-spc link Reconcile a project's full topology after grouping changes
|
|
15
|
+
[--project <project-id>]
|
|
16
|
+
ctrl-spc fetch Clone and link one unavailable project repository
|
|
17
|
+
[repository-id] [--project <project-id>] [--destination <path>] [--yes]
|
|
18
|
+
ctrl-spc Run the local agent process (presence + MCP server)
|
|
19
|
+
ctrl-spc --help Show this help text
|
|
20
|
+
`;
|
|
21
|
+
export async function main(argv = process.argv.slice(2)) {
|
|
22
|
+
const command = argv[0];
|
|
23
|
+
switch (command) {
|
|
24
|
+
case 'login':
|
|
25
|
+
await login();
|
|
26
|
+
return;
|
|
27
|
+
case 'logout':
|
|
28
|
+
logout();
|
|
29
|
+
return;
|
|
30
|
+
case 'init':
|
|
31
|
+
await init(argv.slice(1));
|
|
32
|
+
return;
|
|
33
|
+
case 'link':
|
|
34
|
+
await link(argv.slice(1));
|
|
35
|
+
return;
|
|
36
|
+
case 'fetch':
|
|
37
|
+
await fetchRepository(argv.slice(1));
|
|
38
|
+
return;
|
|
39
|
+
case '--help':
|
|
40
|
+
case '-h':
|
|
41
|
+
console.log(HELP_TEXT);
|
|
42
|
+
return;
|
|
43
|
+
case undefined:
|
|
44
|
+
await run();
|
|
45
|
+
return;
|
|
46
|
+
default:
|
|
47
|
+
console.error(`Unknown command: ${command}\n`);
|
|
48
|
+
console.log(HELP_TEXT);
|
|
49
|
+
process.exitCode = 1;
|
|
50
|
+
return;
|
|
45
51
|
}
|
|
46
|
-
if (command === 'status') {
|
|
47
|
-
const { runStatus } = await import('./lifecycle.js');
|
|
48
|
-
const deps = await lifecycleDeps();
|
|
49
|
-
await runStatus({ createSession: deps.createSession, daemon: deps.daemon, log: deps.log });
|
|
50
|
-
return;
|
|
51
|
-
}
|
|
52
|
-
if (command === 'connect') {
|
|
53
|
-
const { runConnect } = await import('./lifecycle.js');
|
|
54
|
-
await runConnect(await lifecycleDeps());
|
|
55
|
-
return;
|
|
56
|
-
}
|
|
57
|
-
if (command === 'reconnect') {
|
|
58
|
-
const { runReconnect } = await import('./lifecycle.js');
|
|
59
|
-
await runReconnect(await lifecycleDeps());
|
|
60
|
-
return;
|
|
61
|
-
}
|
|
62
|
-
if (command === 'disconnect') {
|
|
63
|
-
const { runDisconnect } = await import('./lifecycle.js');
|
|
64
|
-
await runDisconnect(await lifecycleDeps());
|
|
65
|
-
return;
|
|
66
|
-
}
|
|
67
|
-
console.error('Usage: ctrl-spc <setup|status|connect|reconnect|disconnect|init|serve>');
|
|
68
|
-
process.exitCode = 1;
|
|
69
52
|
}
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
53
|
+
if (!process.env.VITEST) {
|
|
54
|
+
main().catch((err) => {
|
|
55
|
+
console.error(err instanceof Error ? err.message : String(err));
|
|
56
|
+
process.exit(1);
|
|
57
|
+
});
|
|
58
|
+
}
|