@intutic/cli 0.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 (98) hide show
  1. package/README.md +107 -0
  2. package/dist/cli.d.ts +14 -0
  3. package/dist/cli.d.ts.map +1 -0
  4. package/dist/cli.js +92 -0
  5. package/dist/cli.js.map +1 -0
  6. package/dist/commands/connect.d.ts +18 -0
  7. package/dist/commands/connect.d.ts.map +1 -0
  8. package/dist/commands/connect.js +154 -0
  9. package/dist/commands/connect.js.map +1 -0
  10. package/dist/commands/init.d.ts +13 -0
  11. package/dist/commands/init.d.ts.map +1 -0
  12. package/dist/commands/init.js +84 -0
  13. package/dist/commands/init.js.map +1 -0
  14. package/dist/commands/login.d.ts +14 -0
  15. package/dist/commands/login.d.ts.map +1 -0
  16. package/dist/commands/login.js +121 -0
  17. package/dist/commands/login.js.map +1 -0
  18. package/dist/commands/logout.d.ts +8 -0
  19. package/dist/commands/logout.d.ts.map +1 -0
  20. package/dist/commands/logout.js +13 -0
  21. package/dist/commands/logout.js.map +1 -0
  22. package/dist/commands/status.d.ts +11 -0
  23. package/dist/commands/status.d.ts.map +1 -0
  24. package/dist/commands/status.js +55 -0
  25. package/dist/commands/status.js.map +1 -0
  26. package/dist/commands/traces.d.ts +32 -0
  27. package/dist/commands/traces.d.ts.map +1 -0
  28. package/dist/commands/traces.js +207 -0
  29. package/dist/commands/traces.js.map +1 -0
  30. package/dist/commands/whoami.d.ts +12 -0
  31. package/dist/commands/whoami.d.ts.map +1 -0
  32. package/dist/commands/whoami.js +34 -0
  33. package/dist/commands/whoami.js.map +1 -0
  34. package/dist/config/paths.d.ts +30 -0
  35. package/dist/config/paths.d.ts.map +1 -0
  36. package/dist/config/paths.js +53 -0
  37. package/dist/config/paths.js.map +1 -0
  38. package/dist/config/store.d.ts +25 -0
  39. package/dist/config/store.d.ts.map +1 -0
  40. package/dist/config/store.js +69 -0
  41. package/dist/config/store.js.map +1 -0
  42. package/dist/harness/aider.d.ts +12 -0
  43. package/dist/harness/aider.d.ts.map +1 -0
  44. package/dist/harness/aider.js +62 -0
  45. package/dist/harness/aider.js.map +1 -0
  46. package/dist/harness/antigravity.d.ts +12 -0
  47. package/dist/harness/antigravity.d.ts.map +1 -0
  48. package/dist/harness/antigravity.js +69 -0
  49. package/dist/harness/antigravity.js.map +1 -0
  50. package/dist/harness/base.d.ts +20 -0
  51. package/dist/harness/base.d.ts.map +1 -0
  52. package/dist/harness/base.js +73 -0
  53. package/dist/harness/base.js.map +1 -0
  54. package/dist/harness/claudeCode.d.ts +2 -0
  55. package/dist/harness/claudeCode.d.ts.map +1 -0
  56. package/dist/harness/claudeCode.js +9 -0
  57. package/dist/harness/claudeCode.js.map +1 -0
  58. package/dist/harness/codex.d.ts +13 -0
  59. package/dist/harness/codex.d.ts.map +1 -0
  60. package/dist/harness/codex.js +74 -0
  61. package/dist/harness/codex.js.map +1 -0
  62. package/dist/harness/cursor.d.ts +2 -0
  63. package/dist/harness/cursor.d.ts.map +1 -0
  64. package/dist/harness/cursor.js +9 -0
  65. package/dist/harness/cursor.js.map +1 -0
  66. package/dist/harness/detector.d.ts +29 -0
  67. package/dist/harness/detector.d.ts.map +1 -0
  68. package/dist/harness/detector.js +61 -0
  69. package/dist/harness/detector.js.map +1 -0
  70. package/dist/harness/n8n.d.ts +13 -0
  71. package/dist/harness/n8n.d.ts.map +1 -0
  72. package/dist/harness/n8n.js +30 -0
  73. package/dist/harness/n8n.js.map +1 -0
  74. package/dist/harness/openhands.d.ts +12 -0
  75. package/dist/harness/openhands.d.ts.map +1 -0
  76. package/dist/harness/openhands.js +63 -0
  77. package/dist/harness/openhands.js.map +1 -0
  78. package/dist/harness/types.d.ts +30 -0
  79. package/dist/harness/types.d.ts.map +1 -0
  80. package/dist/harness/types.js +25 -0
  81. package/dist/harness/types.js.map +1 -0
  82. package/dist/harness/windsurf.d.ts +2 -0
  83. package/dist/harness/windsurf.d.ts.map +1 -0
  84. package/dist/harness/windsurf.js +9 -0
  85. package/dist/harness/windsurf.js.map +1 -0
  86. package/dist/lib/api.d.ts +44 -0
  87. package/dist/lib/api.d.ts.map +1 -0
  88. package/dist/lib/api.js +55 -0
  89. package/dist/lib/api.js.map +1 -0
  90. package/dist/lib/hash.d.ts +11 -0
  91. package/dist/lib/hash.d.ts.map +1 -0
  92. package/dist/lib/hash.js +18 -0
  93. package/dist/lib/hash.js.map +1 -0
  94. package/dist/lib/logger.d.ts +18 -0
  95. package/dist/lib/logger.d.ts.map +1 -0
  96. package/dist/lib/logger.js +33 -0
  97. package/dist/lib/logger.js.map +1 -0
  98. package/package.json +31 -0
package/README.md ADDED
@@ -0,0 +1,107 @@
1
+ # @intutic/cli
2
+
3
+ > AI governance control plane for developer workspaces.
4
+
5
+ Intutic intercepts agent-to-LLM traffic, enforces SOPs, detects anomalies, and tracks costs — all without changing how your team codes.
6
+
7
+ ## Quick Start
8
+
9
+ ```bash
10
+ # Install globally
11
+ npm install -g @intutic/cli
12
+
13
+ # Or run directly
14
+ npx @intutic/cli init
15
+ ```
16
+
17
+ **5 minutes to your first governance verdict:**
18
+
19
+ ```bash
20
+ intutic login # Authenticate with control plane
21
+ intutic init # Detect harnesses, configure proxy
22
+ intutic connect # Start sync daemon (background)
23
+ intutic traces list # See governance verdicts
24
+ ```
25
+
26
+ ## Commands
27
+
28
+ | Command | Description |
29
+ |---------|-------------|
30
+ | `intutic init` | Initialize workspace — detect harnesses, configure sync |
31
+ | `intutic login` | Authenticate with control plane (`--api-key`, `--dev`) |
32
+ | `intutic logout` | Clear stored credentials |
33
+ | `intutic status` | Show workspace status — auth, harnesses, sync state |
34
+ | `intutic whoami` | Show current authenticated identity |
35
+ | `intutic connect` | Start sync daemon — bidirectional config sync (`--interval`) |
36
+ | `intutic traces list` | List execution traces (`--limit`, `--since`, `--action`, `--model`, `--json`) |
37
+ | `intutic traces inspect <id>` | Show full detail of a single trace |
38
+
39
+ ## Supported Harnesses
40
+
41
+ Intutic auto-detects and configures these AI coding harnesses:
42
+
43
+ | Harness | Detection | Config Method |
44
+ |---------|-----------|---------------|
45
+ | **Claude Code** | `~/.claude/` directory | `CLAUDE.md` + `settings.json` |
46
+ | **Cursor** | `~/.cursor/` directory | `.cursorrules` file |
47
+ | **Aider** | `~/.aider/` directory | `.aider.conf.yml` |
48
+ | **Antigravity** | `~/.gemini/` directory | Environment variables |
49
+ | **Codex** | `~/.codex/` directory | `codex.json` config |
50
+ | **n8n** | Running n8n instance | API integration (stub) |
51
+ | **OpenClaw** | `~/.openclaw/` directory | `config.yaml` |
52
+ | **Hermes** | `~/.hermes/` directory | `config.toml` |
53
+
54
+ ## Trace Inspection
55
+
56
+ View governance verdicts directly from the terminal:
57
+
58
+ ```bash
59
+ # List recent traces (last 24h)
60
+ intutic traces list
61
+
62
+ # Filter by enforcement action
63
+ intutic traces list --action KILL
64
+
65
+ # Filter by model and time window
66
+ intutic traces list --model claude-4 --since 7d
67
+
68
+ # JSON output for scripting
69
+ intutic traces list --json | jq '.traces[] | select(.enforcementAction == "HIJACK")'
70
+
71
+ # Inspect a specific trace
72
+ intutic traces inspect tr_abc123
73
+ ```
74
+
75
+ ## Configuration
76
+
77
+ Credentials are stored at `~/.intutic/credentials.json` (mode `0600`).
78
+
79
+ ### Environment Variables
80
+
81
+ | Variable | Description |
82
+ |----------|-------------|
83
+ | `INTUTIC_API_URL` | Control plane URL (default: `https://api.intutic.ai`) |
84
+ | `INTUTIC_API_KEY` | API key for non-interactive auth (`vk_*` prefix) |
85
+
86
+ ### Development Mode
87
+
88
+ Use `--dev` flag to point at local control plane (`http://localhost:3001`):
89
+
90
+ ```bash
91
+ intutic login --dev
92
+ intutic connect --dev
93
+ intutic traces list --dev
94
+ ```
95
+
96
+ ## Documentation
97
+
98
+ Full documentation: [docs.intutic.ai](https://docs.intutic.ai)
99
+
100
+ - [Getting Started](https://docs.intutic.ai/guide/getting-started)
101
+ - [Integration Guides](https://docs.intutic.ai/integrations/)
102
+ - [CLI Reference](https://docs.intutic.ai/reference/cli)
103
+ - [API Reference](https://docs.intutic.ai/reference/api)
104
+
105
+ ## License
106
+
107
+ Proprietary — see [LICENSE](../../LICENSE) for details.
package/dist/cli.d.ts ADDED
@@ -0,0 +1,14 @@
1
+ #!/usr/bin/env node
2
+ /**
3
+ * Intutic CLI — Entry point
4
+ *
5
+ * AI governance control plane for developer workspaces.
6
+ * Provides harness detection, config sync, and workspace management.
7
+ *
8
+ * LLD #8 — Sync Daemon / CLI
9
+ * HLD §3.14 — Real-Time State Mirroring
10
+ *
11
+ * @module
12
+ */
13
+ export {};
14
+ //# sourceMappingURL=cli.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"cli.d.ts","sourceRoot":"","sources":["../src/cli.ts"],"names":[],"mappings":";AACA;;;;;;;;;;GAUG"}
package/dist/cli.js ADDED
@@ -0,0 +1,92 @@
1
+ #!/usr/bin/env node
2
+ /**
3
+ * Intutic CLI — Entry point
4
+ *
5
+ * AI governance control plane for developer workspaces.
6
+ * Provides harness detection, config sync, and workspace management.
7
+ *
8
+ * LLD #8 — Sync Daemon / CLI
9
+ * HLD §3.14 — Real-Time State Mirroring
10
+ *
11
+ * @module
12
+ */
13
+ import { Command } from 'commander';
14
+ const program = new Command();
15
+ program
16
+ .name('intutic')
17
+ .description('Intutic CLI — AI governance control plane for developer workspaces')
18
+ .version('0.1.0');
19
+ program
20
+ .command('init')
21
+ .description('Initialize workspace — detect harnesses, configure sync')
22
+ .option('--dev', 'Use local control plane (http://localhost:3001)')
23
+ .action(async (opts) => {
24
+ const { runInit } = await import('./commands/init.js');
25
+ await runInit(opts);
26
+ });
27
+ program
28
+ .command('login')
29
+ .description('Authenticate with the Intutic control plane')
30
+ .option('--api-key <key>', 'Authenticate with an API key (vk_*)')
31
+ .option('--dev', 'Use local control plane (http://localhost:3001)')
32
+ .action(async (opts) => {
33
+ const { runLogin } = await import('./commands/login.js');
34
+ await runLogin(opts);
35
+ });
36
+ program
37
+ .command('logout')
38
+ .description('Clear stored credentials')
39
+ .action(async () => {
40
+ const { runLogout } = await import('./commands/logout.js');
41
+ await runLogout();
42
+ });
43
+ program
44
+ .command('status')
45
+ .description('Show workspace status — auth, harnesses, sync state')
46
+ .action(async () => {
47
+ const { runStatus } = await import('./commands/status.js');
48
+ await runStatus();
49
+ });
50
+ program
51
+ .command('whoami')
52
+ .description('Show current authenticated identity')
53
+ .option('--dev', 'Use local control plane (http://localhost:3001)')
54
+ .action(async (opts) => {
55
+ const { runWhoami } = await import('./commands/whoami.js');
56
+ await runWhoami(opts);
57
+ });
58
+ program
59
+ .command('connect')
60
+ .description('Start sync daemon — bidirectional config sync with control plane')
61
+ .option('--dev', 'Use local control plane (http://localhost:3001)')
62
+ .option('--interval <ms>', 'Poll interval in milliseconds', '30000')
63
+ .action(async (opts) => {
64
+ const { runConnect } = await import('./commands/connect.js');
65
+ await runConnect(opts);
66
+ });
67
+ const traces = program
68
+ .command('traces')
69
+ .description('Query execution traces — list, filter, and inspect');
70
+ traces
71
+ .command('list')
72
+ .description('List execution traces for the workspace')
73
+ .option('--limit <n>', 'Number of traces to show (default: 20, max: 100)')
74
+ .option('--since <duration>', 'Time window, e.g. "24h", "7d", "30m" (default: "24h")')
75
+ .option('--action <type>', 'Filter by enforcement action (BYPASS|ENHANCE|HIJACK|KILL)')
76
+ .option('--model <name>', 'Filter by model name')
77
+ .option('--json', 'Output as JSON instead of table')
78
+ .option('--dev', 'Use local control plane (http://localhost:3001)')
79
+ .action(async (opts) => {
80
+ const { runTracesList } = await import('./commands/traces.js');
81
+ await runTracesList(opts);
82
+ });
83
+ traces
84
+ .command('inspect <trace_id>')
85
+ .description('Show full detail of a single trace')
86
+ .option('--dev', 'Use local control plane (http://localhost:3001)')
87
+ .action(async (traceId, opts) => {
88
+ const { runTracesInspect } = await import('./commands/traces.js');
89
+ await runTracesInspect(traceId, opts);
90
+ });
91
+ program.parse();
92
+ //# sourceMappingURL=cli.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"cli.js","sourceRoot":"","sources":["../src/cli.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,18 @@
1
+ /**
2
+ * `intutic connect` — Start the sync daemon.
3
+ *
4
+ * Runs a 30-second polling loop that:
5
+ * 1. Fetches config from control plane (active SOPs, proxy URL)
6
+ * 2. Writes SOP content to harness config files
7
+ * 3. Computes SHA-256 hashes of written files
8
+ * 4. Reports hashes + status to control plane
9
+ *
10
+ * HLD §3.14 — Real-Time State Mirroring
11
+ * LLD #8 — Sync Daemon / CLI
12
+ * @module
13
+ */
14
+ export declare function runConnect(opts: {
15
+ dev?: boolean;
16
+ interval?: string;
17
+ }): Promise<void>;
18
+ //# sourceMappingURL=connect.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"connect.d.ts","sourceRoot":"","sources":["../../src/commands/connect.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;GAYG;AAcH,wBAAsB,UAAU,CAAC,IAAI,EAAE;IAAE,GAAG,CAAC,EAAE,OAAO,CAAC;IAAC,QAAQ,CAAC,EAAE,MAAM,CAAA;CAAE,GAAG,OAAO,CAAC,IAAI,CAAC,CAgK1F"}
@@ -0,0 +1,154 @@
1
+ /**
2
+ * `intutic connect` — Start the sync daemon.
3
+ *
4
+ * Runs a 30-second polling loop that:
5
+ * 1. Fetches config from control plane (active SOPs, proxy URL)
6
+ * 2. Writes SOP content to harness config files
7
+ * 3. Computes SHA-256 hashes of written files
8
+ * 4. Reports hashes + status to control plane
9
+ *
10
+ * HLD §3.14 — Real-Time State Mirroring
11
+ * LLD #8 — Sync Daemon / CLI
12
+ * @module
13
+ */
14
+ import { log } from '../lib/logger.js';
15
+ import { loadCredentials, loadConfig, saveConfig, loadIntegrity, saveIntegrity } from '../config/store.js';
16
+ import { resolveControlPlaneUrl } from '../config/paths.js';
17
+ import { createApiClient } from '../lib/api.js';
18
+ import { getAdapter } from '../harness/detector.js';
19
+ import { newIso } from '@intutic/id';
20
+ import pc from 'picocolors';
21
+ const DEFAULT_POLL_INTERVAL = 30_000;
22
+ export async function runConnect(opts) {
23
+ // 1. Load credentials + config
24
+ const creds = loadCredentials();
25
+ if (!creds) {
26
+ log.error('Not authenticated. Run `intutic login` first.');
27
+ process.exit(1);
28
+ }
29
+ const config = loadConfig();
30
+ if (!config) {
31
+ log.error('Workspace not initialized. Run `intutic init` first.');
32
+ process.exit(1);
33
+ }
34
+ const devMode = opts.dev || process.env.INTUTIC_DEV === '1' || config.devMode;
35
+ const controlPlaneUrl = resolveControlPlaneUrl(devMode);
36
+ const pollInterval = opts.interval ? parseInt(opts.interval, 10) : DEFAULT_POLL_INTERVAL;
37
+ const connectedSince = newIso();
38
+ const client = createApiClient(controlPlaneUrl, creds.apiKey);
39
+ log.header('Intutic — Sync Daemon');
40
+ log.field('Workspace', creds.workspaceId);
41
+ log.field('Control Plane', controlPlaneUrl);
42
+ log.field('Poll Interval', `${pollInterval / 1000}s`);
43
+ log.field('Harnesses', config.harnesses.join(', ') || '(none)');
44
+ console.log('');
45
+ log.info('Starting sync loop... (Ctrl+C to stop)');
46
+ console.log('');
47
+ // 2. AbortController for clean shutdown
48
+ const ac = new AbortController();
49
+ const shutdown = () => {
50
+ log.info('Shutting down...');
51
+ ac.abort();
52
+ };
53
+ process.on('SIGINT', shutdown);
54
+ process.on('SIGTERM', shutdown);
55
+ // 3. Sync loop
56
+ let localConfigVersion = config.configVersion;
57
+ while (!ac.signal.aborted) {
58
+ try {
59
+ // a. Fetch config from control plane
60
+ const syncConfig = await client.fetchConfig(creds.workspaceId);
61
+ let sopsWritten = 0;
62
+ // b. If config version is newer, write to harness files
63
+ if (syncConfig.configVersion > localConfigVersion) {
64
+ for (const harnessType of config.harnesses) {
65
+ const adapter = getAdapter(harnessType);
66
+ if (!adapter)
67
+ continue;
68
+ const targetSops = syncConfig.sops.filter((sop) => sop.harnessTargets.includes(harnessType));
69
+ if (targetSops.length === 0)
70
+ continue;
71
+ const written = await adapter.writeConfig(config.workspaceRoot, targetSops, syncConfig.proxyUrl);
72
+ if (written) {
73
+ sopsWritten += targetSops.length;
74
+ }
75
+ }
76
+ localConfigVersion = syncConfig.configVersion;
77
+ saveConfig({ ...config, configVersion: localConfigVersion });
78
+ }
79
+ // c. Compute file hashes + update integrity store
80
+ const fileHashes = [];
81
+ const canonicalHashes = {};
82
+ // Build canonical hash map from synced SOPs
83
+ const integrity = loadIntegrity(config.workspaceRoot);
84
+ if (integrity) {
85
+ Object.assign(canonicalHashes, integrity.files);
86
+ }
87
+ for (const harnessType of config.harnesses) {
88
+ const adapter = getAdapter(harnessType);
89
+ if (!adapter || !adapter.configFileName)
90
+ continue;
91
+ const currentHash = await adapter.readCurrentHash(config.workspaceRoot);
92
+ if (!currentHash)
93
+ continue;
94
+ const canonical = canonicalHashes[adapter.configFileName] ?? currentHash;
95
+ fileHashes.push({
96
+ filePath: adapter.configFileName,
97
+ localHash: currentHash,
98
+ canonicalHash: canonical,
99
+ drifted: currentHash !== canonical,
100
+ });
101
+ // Update canonical hash to current
102
+ canonicalHashes[adapter.configFileName] = currentHash;
103
+ }
104
+ // Save integrity store
105
+ saveIntegrity(config.workspaceRoot, {
106
+ lastSyncAt: newIso(),
107
+ configVersion: localConfigVersion,
108
+ files: canonicalHashes,
109
+ });
110
+ // d. Report hashes to control plane
111
+ let driftCount = 0;
112
+ if (fileHashes.length > 0) {
113
+ const hashReport = await client.reportHashes({
114
+ workspaceId: creds.workspaceId,
115
+ harnessType: config.harnesses[0],
116
+ files: fileHashes,
117
+ reportedAt: newIso(),
118
+ });
119
+ driftCount = hashReport.driftCount;
120
+ }
121
+ // e. Report status
122
+ await client.reportStatus({
123
+ workspaceId: creds.workspaceId,
124
+ configVersion: localConfigVersion,
125
+ connectedSince,
126
+ lastSyncAt: newIso(),
127
+ harnesses: config.harnesses.map((h) => ({
128
+ type: h,
129
+ configPath: getAdapter(h)?.configFileName ?? '',
130
+ detected: true,
131
+ lastWriteAt: sopsWritten > 0 ? newIso() : null,
132
+ })),
133
+ });
134
+ // f. Log result
135
+ const driftLabel = driftCount > 0 ? pc.yellow(` — ${driftCount} drift(s)`) : '';
136
+ log.dim(`[sync] Config v${localConfigVersion} — ${sopsWritten} SOPs synced${driftLabel} — next check in ${pollInterval / 1000}s`);
137
+ }
138
+ catch (err) {
139
+ // Per-iteration error — log and continue
140
+ log.error(`Sync failed: ${err instanceof Error ? err.message : String(err)}`);
141
+ log.dim('Will retry next interval...');
142
+ }
143
+ // g. Sleep until next poll (or abort)
144
+ await new Promise((resolve) => {
145
+ const timer = setTimeout(resolve, pollInterval);
146
+ ac.signal.addEventListener('abort', () => {
147
+ clearTimeout(timer);
148
+ resolve();
149
+ }, { once: true });
150
+ });
151
+ }
152
+ log.success('Sync daemon stopped.');
153
+ }
154
+ //# sourceMappingURL=connect.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"connect.js","sourceRoot":"","sources":["../../src/commands/connect.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,13 @@
1
+ /**
2
+ * `intutic init` — Initialize workspace.
3
+ *
4
+ * Detects workspace root, auto-detects harnesses, validates
5
+ * credentials, and writes local config.
6
+ *
7
+ * LLD #8 — Sync Daemon / CLI
8
+ * @module
9
+ */
10
+ export declare function runInit(opts: {
11
+ dev?: boolean;
12
+ }): Promise<void>;
13
+ //# sourceMappingURL=init.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"init.d.ts","sourceRoot":"","sources":["../../src/commands/init.ts"],"names":[],"mappings":"AAAA;;;;;;;;GAQG;AA2BH,wBAAsB,OAAO,CAAC,IAAI,EAAE;IAAE,GAAG,CAAC,EAAE,OAAO,CAAA;CAAE,GAAG,OAAO,CAAC,IAAI,CAAC,CAuDpE"}
@@ -0,0 +1,84 @@
1
+ /**
2
+ * `intutic init` — Initialize workspace.
3
+ *
4
+ * Detects workspace root, auto-detects harnesses, validates
5
+ * credentials, and writes local config.
6
+ *
7
+ * LLD #8 — Sync Daemon / CLI
8
+ * @module
9
+ */
10
+ import { existsSync } from 'node:fs';
11
+ import { join, resolve, dirname } from 'node:path';
12
+ import { log } from '../lib/logger.js';
13
+ import { loadCredentials, saveConfig } from '../config/store.js';
14
+ import { detectHarnesses } from '../harness/detector.js';
15
+ import pc from 'picocolors';
16
+ /**
17
+ * Walk up from cwd looking for .git/ or package.json to find workspace root.
18
+ */
19
+ function findWorkspaceRoot() {
20
+ let dir = process.cwd();
21
+ const root = resolve('/');
22
+ while (dir !== root) {
23
+ if (existsSync(join(dir, '.git')) || existsSync(join(dir, 'package.json'))) {
24
+ return dir;
25
+ }
26
+ const parent = dirname(dir);
27
+ if (parent === dir)
28
+ break;
29
+ dir = parent;
30
+ }
31
+ return null;
32
+ }
33
+ export async function runInit(opts) {
34
+ log.header('Intutic — Workspace Initialization');
35
+ // 1. Find workspace root
36
+ const workspaceRoot = findWorkspaceRoot();
37
+ if (!workspaceRoot) {
38
+ log.error('Could not find workspace root (no .git/ or package.json found)');
39
+ log.dim('Run this command from within a project directory.');
40
+ process.exit(1);
41
+ }
42
+ log.success(`Workspace root: ${workspaceRoot}`);
43
+ // 2. Detect harnesses
44
+ log.info('Detecting AI harnesses...');
45
+ const harnesses = await detectHarnesses(workspaceRoot);
46
+ const detected = harnesses.filter((h) => h.detected);
47
+ const notDetected = harnesses.filter((h) => !h.detected);
48
+ console.log('');
49
+ for (const h of detected) {
50
+ console.log(` ${pc.green('✔')} ${pc.bold(h.type)} ${pc.dim(`→ ${h.configPath}`)}`);
51
+ }
52
+ for (const h of notDetected) {
53
+ console.log(` ${pc.dim('○')} ${pc.dim(h.type)} ${pc.dim('(not detected)')}`);
54
+ }
55
+ console.log('');
56
+ if (detected.length === 0) {
57
+ log.warn('No harnesses detected. Intutic will still work via proxy redirect.');
58
+ }
59
+ else {
60
+ log.success(`Detected ${detected.length} harness${detected.length > 1 ? 'es' : ''}`);
61
+ }
62
+ // 3. Check credentials
63
+ const creds = loadCredentials();
64
+ if (!creds) {
65
+ log.warn('Not authenticated. Run `intutic login` to connect to the control plane.');
66
+ }
67
+ else {
68
+ log.success(`Authenticated as ${creds.email}`);
69
+ }
70
+ // 4. Write config
71
+ const devMode = opts.dev || process.env.INTUTIC_DEV === '1';
72
+ saveConfig({
73
+ workspaceRoot,
74
+ harnesses: detected.map((h) => h.type),
75
+ configVersion: 0,
76
+ devMode,
77
+ });
78
+ log.success('Workspace initialized.');
79
+ if (devMode) {
80
+ log.dim('Dev mode: using local control plane (http://localhost:3001)');
81
+ }
82
+ log.dim('Run `intutic connect` to start syncing governance rules.');
83
+ }
84
+ //# sourceMappingURL=init.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"init.js","sourceRoot":"","sources":["../../src/commands/init.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,14 @@
1
+ /**
2
+ * `intutic login` — Authenticate with the control plane.
3
+ *
4
+ * Supports API key (--api-key vk_...) or email+password.
5
+ * Stores credentials at ~/.intutic/credentials.json (mode 0o600).
6
+ *
7
+ * LLD #8 — Sync Daemon / CLI
8
+ * @module
9
+ */
10
+ export declare function runLogin(opts: {
11
+ apiKey?: string;
12
+ dev?: boolean;
13
+ }): Promise<void>;
14
+ //# sourceMappingURL=login.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"login.d.ts","sourceRoot":"","sources":["../../src/commands/login.ts"],"names":[],"mappings":"AAAA;;;;;;;;GAQG;AAsDH,wBAAsB,QAAQ,CAAC,IAAI,EAAE;IAAE,MAAM,CAAC,EAAE,MAAM,CAAC;IAAC,GAAG,CAAC,EAAE,OAAO,CAAA;CAAE,GAAG,OAAO,CAAC,IAAI,CAAC,CA6DtF"}
@@ -0,0 +1,121 @@
1
+ /**
2
+ * `intutic login` — Authenticate with the control plane.
3
+ *
4
+ * Supports API key (--api-key vk_...) or email+password.
5
+ * Stores credentials at ~/.intutic/credentials.json (mode 0o600).
6
+ *
7
+ * LLD #8 — Sync Daemon / CLI
8
+ * @module
9
+ */
10
+ import { log } from '../lib/logger.js';
11
+ import { saveCredentials } from '../config/store.js';
12
+ import { resolveControlPlaneUrl } from '../config/paths.js';
13
+ import { createApiClient } from '../lib/api.js';
14
+ import { newIso } from '@intutic/id';
15
+ import { createInterface } from 'node:readline';
16
+ /**
17
+ * Simple readline prompt (no external deps).
18
+ * For password input, uses muted output.
19
+ */
20
+ async function prompt(question, hidden = false) {
21
+ const rl = createInterface({
22
+ input: process.stdin,
23
+ output: process.stdout,
24
+ });
25
+ return new Promise((resolve) => {
26
+ if (hidden && process.stdin.isTTY) {
27
+ // Mute output for password entry
28
+ process.stdout.write(question);
29
+ let input = '';
30
+ process.stdin.setRawMode(true);
31
+ process.stdin.resume();
32
+ process.stdin.on('data', (char) => {
33
+ const c = char.toString();
34
+ if (c === '\n' || c === '\r') {
35
+ process.stdin.setRawMode(false);
36
+ process.stdin.pause();
37
+ console.log('');
38
+ rl.close();
39
+ resolve(input);
40
+ }
41
+ else if (c === '\u0003') {
42
+ // Ctrl+C
43
+ process.exit(0);
44
+ }
45
+ else if (c === '\u007f') {
46
+ // Backspace
47
+ input = input.slice(0, -1);
48
+ }
49
+ else {
50
+ input += c;
51
+ process.stdout.write('*');
52
+ }
53
+ });
54
+ }
55
+ else {
56
+ rl.question(question, (answer) => {
57
+ rl.close();
58
+ resolve(answer);
59
+ });
60
+ }
61
+ });
62
+ }
63
+ export async function runLogin(opts) {
64
+ log.header('Intutic — Authentication');
65
+ const controlPlaneUrl = resolveControlPlaneUrl(opts.dev);
66
+ log.dim(`Control plane: ${controlPlaneUrl}`);
67
+ if (opts.apiKey) {
68
+ // API key flow
69
+ if (!opts.apiKey.startsWith('vk_')) {
70
+ log.error('API key must start with "vk_"');
71
+ process.exit(1);
72
+ }
73
+ log.info('Validating API key...');
74
+ const client = createApiClient(controlPlaneUrl, opts.apiKey);
75
+ try {
76
+ const me = await client.getMe();
77
+ saveCredentials({
78
+ apiKey: opts.apiKey,
79
+ workspaceId: me.workspaceId,
80
+ controlPlaneUrl,
81
+ email: me.email,
82
+ storedAt: newIso(),
83
+ });
84
+ log.success(`Authenticated as ${me.email}`);
85
+ log.field('Workspace', me.workspaceId);
86
+ log.field('Role', me.role);
87
+ }
88
+ catch (err) {
89
+ log.error(`API key validation failed: ${err instanceof Error ? err.message : String(err)}`);
90
+ process.exit(1);
91
+ }
92
+ }
93
+ else {
94
+ // Email + password flow
95
+ const email = await prompt('Email: ');
96
+ const password = await prompt('Password: ', true);
97
+ if (!email || !password) {
98
+ log.error('Email and password are required.');
99
+ process.exit(1);
100
+ }
101
+ log.info('Authenticating...');
102
+ const client = createApiClient(controlPlaneUrl, ''); // No token yet
103
+ try {
104
+ const result = await client.login(email, password);
105
+ saveCredentials({
106
+ apiKey: result.accessToken,
107
+ workspaceId: result.workspaceId,
108
+ controlPlaneUrl,
109
+ email: result.email,
110
+ storedAt: newIso(),
111
+ });
112
+ log.success(`Authenticated as ${result.email}`);
113
+ log.field('Workspace', result.workspaceId);
114
+ }
115
+ catch (err) {
116
+ log.error(`Login failed: ${err instanceof Error ? err.message : String(err)}`);
117
+ process.exit(1);
118
+ }
119
+ }
120
+ }
121
+ //# sourceMappingURL=login.js.map