codiedev 0.9.25 → 0.9.28
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/README.md +32 -4
- package/assets/opencode-plugin.mjs +54 -0
- package/assets/tui-brand-plugin.tsx +63 -0
- package/dist/backup/cache.js +2 -2
- package/dist/backup/run.js +5 -0
- package/dist/captureExclusions.d.ts +30 -0
- package/dist/captureExclusions.js +118 -0
- package/dist/captureLog.js +6 -3
- package/dist/cli.js +38 -0
- package/dist/codie.d.ts +2 -0
- package/dist/codie.js +233 -0
- package/dist/codieLauncher.d.ts +41 -0
- package/dist/codieLauncher.js +352 -0
- package/dist/codieRuntime.d.ts +19 -0
- package/dist/codieRuntime.js +91 -0
- package/dist/codieRuntimeInstall.d.ts +27 -0
- package/dist/codieRuntimeInstall.js +145 -0
- package/dist/commands/capture.d.ts +14 -0
- package/dist/commands/capture.js +148 -0
- package/dist/configDir.d.ts +1 -0
- package/dist/configDir.js +54 -0
- package/dist/connect.js +27 -2
- package/dist/hook.js +77 -43
- package/dist/upgradeNudge.js +6 -5
- package/dist/utils.d.ts +30 -3
- package/dist/utils.js +313 -22
- package/package.json +13 -5
|
@@ -0,0 +1,91 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
// Where the Codie harness gets its engine.
|
|
3
|
+
//
|
|
4
|
+
// The engine (`opencode-ai`) is a ~132MB platform binary. It is deliberately
|
|
5
|
+
// NOT a dependency of this package: the overwhelming majority of installs are
|
|
6
|
+
// capture-only (Claude Code, Codex, Cursor) and must not pay for a harness
|
|
7
|
+
// they never launch. So `npm i -g codiedev` stays small, and the first `codie`
|
|
8
|
+
// run downloads the engine into the Codiedev state dir.
|
|
9
|
+
//
|
|
10
|
+
// Resolution is pure and injectable so the precedence can be tested without
|
|
11
|
+
// touching the network or a real 132MB install.
|
|
12
|
+
var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
|
|
13
|
+
if (k2 === undefined) k2 = k;
|
|
14
|
+
var desc = Object.getOwnPropertyDescriptor(m, k);
|
|
15
|
+
if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
|
|
16
|
+
desc = { enumerable: true, get: function() { return m[k]; } };
|
|
17
|
+
}
|
|
18
|
+
Object.defineProperty(o, k2, desc);
|
|
19
|
+
}) : (function(o, m, k, k2) {
|
|
20
|
+
if (k2 === undefined) k2 = k;
|
|
21
|
+
o[k2] = m[k];
|
|
22
|
+
}));
|
|
23
|
+
var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
|
|
24
|
+
Object.defineProperty(o, "default", { enumerable: true, value: v });
|
|
25
|
+
}) : function(o, v) {
|
|
26
|
+
o["default"] = v;
|
|
27
|
+
});
|
|
28
|
+
var __importStar = (this && this.__importStar) || (function () {
|
|
29
|
+
var ownKeys = function(o) {
|
|
30
|
+
ownKeys = Object.getOwnPropertyNames || function (o) {
|
|
31
|
+
var ar = [];
|
|
32
|
+
for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k;
|
|
33
|
+
return ar;
|
|
34
|
+
};
|
|
35
|
+
return ownKeys(o);
|
|
36
|
+
};
|
|
37
|
+
return function (mod) {
|
|
38
|
+
if (mod && mod.__esModule) return mod;
|
|
39
|
+
var result = {};
|
|
40
|
+
if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]);
|
|
41
|
+
__setModuleDefault(result, mod);
|
|
42
|
+
return result;
|
|
43
|
+
};
|
|
44
|
+
})();
|
|
45
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
46
|
+
exports.CODIE_MIN_NODE_MAJOR = exports.OPENCODE_RUNTIME_SPEC = void 0;
|
|
47
|
+
exports.codieRuntimeDir = codieRuntimeDir;
|
|
48
|
+
exports.resolveCodieRuntime = resolveCodieRuntime;
|
|
49
|
+
exports.assertCodieNodeVersion = assertCodieNodeVersion;
|
|
50
|
+
const path = __importStar(require("path"));
|
|
51
|
+
// Pinned exactly, not a range. patchBrandWordmark() only rewrites bundles it
|
|
52
|
+
// recognizes byte-for-byte, so a floating version would silently regress the
|
|
53
|
+
// terminal branding whenever OpenCode ships a new build.
|
|
54
|
+
exports.OPENCODE_RUNTIME_SPEC = "opencode-ai@1.18.4";
|
|
55
|
+
// The harness needs 20; capture does not. package.json `engines` stays >=18 so
|
|
56
|
+
// `codiedev connect` still installs for Node 18 users — this floor is enforced
|
|
57
|
+
// at `codie` launch instead.
|
|
58
|
+
exports.CODIE_MIN_NODE_MAJOR = 20;
|
|
59
|
+
function codieRuntimeDir(stateDir) {
|
|
60
|
+
return path.join(stateDir, "runtime");
|
|
61
|
+
}
|
|
62
|
+
function installedBinary(stateDir) {
|
|
63
|
+
return path.join(codieRuntimeDir(stateDir), "node_modules", "opencode-ai", "bin", "opencode.exe");
|
|
64
|
+
}
|
|
65
|
+
function resolveCodieRuntime({ env, stateDir, existsSync, resolveBundled, }) {
|
|
66
|
+
// 1. Explicit override — the air-gapped / pre-provisioned escape hatch.
|
|
67
|
+
const override = env.CODIEDEV_OPENCODE_BINARY?.trim();
|
|
68
|
+
if (override) {
|
|
69
|
+
if (!existsSync(override)) {
|
|
70
|
+
throw new Error(`CODIEDEV_OPENCODE_BINARY points at a missing file: ${override}`);
|
|
71
|
+
}
|
|
72
|
+
return { kind: "ready", binary: override };
|
|
73
|
+
}
|
|
74
|
+
// 2. A bundled engine, if this package was installed with one (dev checkout,
|
|
75
|
+
// or a future vendored distribution).
|
|
76
|
+
const bundled = resolveBundled();
|
|
77
|
+
if (bundled)
|
|
78
|
+
return { kind: "ready", binary: bundled };
|
|
79
|
+
// 3. The lazily installed engine.
|
|
80
|
+
const binary = installedBinary(stateDir);
|
|
81
|
+
if (existsSync(binary))
|
|
82
|
+
return { kind: "ready", binary };
|
|
83
|
+
return { kind: "missing", installDir: codieRuntimeDir(stateDir), binary, spec: exports.OPENCODE_RUNTIME_SPEC };
|
|
84
|
+
}
|
|
85
|
+
function assertCodieNodeVersion(version) {
|
|
86
|
+
const major = Number.parseInt(String(version).split(".")[0], 10);
|
|
87
|
+
if (Number.isFinite(major) && major < exports.CODIE_MIN_NODE_MAJOR) {
|
|
88
|
+
throw new Error(`The Codie harness needs Node ${exports.CODIE_MIN_NODE_MAJOR} or newer (found ${version}). ` +
|
|
89
|
+
`Session capture still works on this version — only \`codie\` requires the upgrade.`);
|
|
90
|
+
}
|
|
91
|
+
}
|
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
import { CodieRuntime } from "./codieRuntime";
|
|
2
|
+
export declare function buildRuntimeInstallArgs(stagingDir: string, spec: string): string[];
|
|
3
|
+
export interface InstallDeps {
|
|
4
|
+
runtime: Extract<CodieRuntime, {
|
|
5
|
+
kind: "missing";
|
|
6
|
+
}>;
|
|
7
|
+
makeStagingDir: () => string;
|
|
8
|
+
run: (args: string[]) => {
|
|
9
|
+
status: number | null;
|
|
10
|
+
stderr: string;
|
|
11
|
+
};
|
|
12
|
+
exists: (candidate: string) => boolean;
|
|
13
|
+
rename: (from: string, to: string) => void;
|
|
14
|
+
remove: (target: string) => void;
|
|
15
|
+
log: (message: string) => void;
|
|
16
|
+
}
|
|
17
|
+
export declare function installCodieRuntime(deps: InstallDeps): void;
|
|
18
|
+
export declare function ensureCodieRuntimeBinary({ resolve, install, }: {
|
|
19
|
+
resolve: () => CodieRuntime;
|
|
20
|
+
install: (runtime: Extract<CodieRuntime, {
|
|
21
|
+
kind: "missing";
|
|
22
|
+
}>) => void;
|
|
23
|
+
}): string;
|
|
24
|
+
export declare function installCodieRuntimeOnDisk(runtime: Extract<CodieRuntime, {
|
|
25
|
+
kind: "missing";
|
|
26
|
+
}>, stateDir: string): void;
|
|
27
|
+
export declare function defaultStagingRoot(): string;
|
|
@@ -0,0 +1,145 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
// First-run install of the OpenCode engine for the Codie harness.
|
|
3
|
+
//
|
|
4
|
+
// Staged then renamed: npm writes into a sibling staging directory and only an
|
|
5
|
+
// install that actually produced the engine binary gets promoted to the real
|
|
6
|
+
// runtime path. An interrupted or half-finished download therefore never
|
|
7
|
+
// leaves a directory that resolveCodieRuntime() would report as "ready".
|
|
8
|
+
var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
|
|
9
|
+
if (k2 === undefined) k2 = k;
|
|
10
|
+
var desc = Object.getOwnPropertyDescriptor(m, k);
|
|
11
|
+
if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
|
|
12
|
+
desc = { enumerable: true, get: function() { return m[k]; } };
|
|
13
|
+
}
|
|
14
|
+
Object.defineProperty(o, k2, desc);
|
|
15
|
+
}) : (function(o, m, k, k2) {
|
|
16
|
+
if (k2 === undefined) k2 = k;
|
|
17
|
+
o[k2] = m[k];
|
|
18
|
+
}));
|
|
19
|
+
var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
|
|
20
|
+
Object.defineProperty(o, "default", { enumerable: true, value: v });
|
|
21
|
+
}) : function(o, v) {
|
|
22
|
+
o["default"] = v;
|
|
23
|
+
});
|
|
24
|
+
var __importStar = (this && this.__importStar) || (function () {
|
|
25
|
+
var ownKeys = function(o) {
|
|
26
|
+
ownKeys = Object.getOwnPropertyNames || function (o) {
|
|
27
|
+
var ar = [];
|
|
28
|
+
for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k;
|
|
29
|
+
return ar;
|
|
30
|
+
};
|
|
31
|
+
return ownKeys(o);
|
|
32
|
+
};
|
|
33
|
+
return function (mod) {
|
|
34
|
+
if (mod && mod.__esModule) return mod;
|
|
35
|
+
var result = {};
|
|
36
|
+
if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]);
|
|
37
|
+
__setModuleDefault(result, mod);
|
|
38
|
+
return result;
|
|
39
|
+
};
|
|
40
|
+
})();
|
|
41
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
42
|
+
exports.buildRuntimeInstallArgs = buildRuntimeInstallArgs;
|
|
43
|
+
exports.installCodieRuntime = installCodieRuntime;
|
|
44
|
+
exports.ensureCodieRuntimeBinary = ensureCodieRuntimeBinary;
|
|
45
|
+
exports.installCodieRuntimeOnDisk = installCodieRuntimeOnDisk;
|
|
46
|
+
exports.defaultStagingRoot = defaultStagingRoot;
|
|
47
|
+
const fs = __importStar(require("fs"));
|
|
48
|
+
const os = __importStar(require("os"));
|
|
49
|
+
const path = __importStar(require("path"));
|
|
50
|
+
const child_process_1 = require("child_process");
|
|
51
|
+
const codieRuntime_1 = require("./codieRuntime");
|
|
52
|
+
function buildRuntimeInstallArgs(stagingDir, spec) {
|
|
53
|
+
// No --registry / --proxy: inherit whatever npm config the developer's
|
|
54
|
+
// machine already has so an internal mirror keeps working.
|
|
55
|
+
return [
|
|
56
|
+
"install",
|
|
57
|
+
"--prefix",
|
|
58
|
+
stagingDir,
|
|
59
|
+
"--no-audit",
|
|
60
|
+
"--no-fund",
|
|
61
|
+
"--omit=dev",
|
|
62
|
+
"--loglevel=error",
|
|
63
|
+
spec,
|
|
64
|
+
];
|
|
65
|
+
}
|
|
66
|
+
function installCodieRuntime(deps) {
|
|
67
|
+
const { runtime, makeStagingDir, run, exists, rename, remove, log } = deps;
|
|
68
|
+
const staging = makeStagingDir();
|
|
69
|
+
log(`Installing the Codie engine (${runtime.spec}). This runs once, and only for \`codie\`.`);
|
|
70
|
+
let result;
|
|
71
|
+
try {
|
|
72
|
+
result = run(buildRuntimeInstallArgs(staging, runtime.spec));
|
|
73
|
+
}
|
|
74
|
+
catch (error) {
|
|
75
|
+
remove(staging);
|
|
76
|
+
throw installFailure(error instanceof Error ? error.message : String(error));
|
|
77
|
+
}
|
|
78
|
+
if (result.status !== 0) {
|
|
79
|
+
remove(staging);
|
|
80
|
+
throw installFailure(result.stderr?.trim() || `npm exited with ${result.status}`);
|
|
81
|
+
}
|
|
82
|
+
// Trust the artifact, not the exit code — a misconfigured mirror can succeed
|
|
83
|
+
// with nothing useful in the tarball.
|
|
84
|
+
const stagedBinary = path.join(staging, "node_modules", "opencode-ai", "bin", "opencode.exe");
|
|
85
|
+
if (!exists(stagedBinary)) {
|
|
86
|
+
remove(staging);
|
|
87
|
+
throw new Error(`The Codie engine install did not produce a runtime binary. ` +
|
|
88
|
+
`Check that ${runtime.spec} is available from your npm registry.`);
|
|
89
|
+
}
|
|
90
|
+
try {
|
|
91
|
+
rename(staging, runtime.installDir);
|
|
92
|
+
}
|
|
93
|
+
finally {
|
|
94
|
+
// No-op after a successful promote (rmSync is force: true); after a failed
|
|
95
|
+
// one it keeps a ~132MB staging dir from being orphaned — mkdtemp mints a
|
|
96
|
+
// fresh sibling every attempt, so leaks would accumulate.
|
|
97
|
+
remove(staging);
|
|
98
|
+
}
|
|
99
|
+
}
|
|
100
|
+
// Resolve the engine, downloading it once if this is the first `codie` run.
|
|
101
|
+
function ensureCodieRuntimeBinary({ resolve, install, }) {
|
|
102
|
+
const runtime = resolve();
|
|
103
|
+
if (runtime.kind === "ready")
|
|
104
|
+
return runtime.binary;
|
|
105
|
+
install(runtime);
|
|
106
|
+
return runtime.binary;
|
|
107
|
+
}
|
|
108
|
+
function installFailure(detail) {
|
|
109
|
+
return new Error(`Could not download the Codie engine: ${detail}\n` +
|
|
110
|
+
`If you are behind a proxy or an internal registry, make sure npm can reach it. ` +
|
|
111
|
+
`To use a pre-provisioned engine instead, set CODIEDEV_OPENCODE_BINARY to its path.`);
|
|
112
|
+
}
|
|
113
|
+
// ── Real-world wiring ───────────────────────────────────────────────────
|
|
114
|
+
// Kept separate from the pure logic above so tests never shell out to npm.
|
|
115
|
+
function installCodieRuntimeOnDisk(runtime, stateDir) {
|
|
116
|
+
installCodieRuntime({
|
|
117
|
+
runtime,
|
|
118
|
+
makeStagingDir: () => {
|
|
119
|
+
// Sibling of the final dir so the promote is a same-filesystem rename.
|
|
120
|
+
fs.mkdirSync(path.dirname((0, codieRuntime_1.codieRuntimeDir)(stateDir)), { recursive: true });
|
|
121
|
+
return fs.mkdtempSync(path.join(path.dirname((0, codieRuntime_1.codieRuntimeDir)(stateDir)), ".runtime-staging-"));
|
|
122
|
+
},
|
|
123
|
+
run: (args) => {
|
|
124
|
+
const npm = process.platform === "win32" ? "npm.cmd" : "npm";
|
|
125
|
+
const result = (0, child_process_1.spawnSync)(npm, args, {
|
|
126
|
+
encoding: "utf8",
|
|
127
|
+
stdio: ["ignore", "inherit", "pipe"],
|
|
128
|
+
env: { ...process.env, npm_config_yes: "true" },
|
|
129
|
+
});
|
|
130
|
+
if (result.error)
|
|
131
|
+
throw result.error;
|
|
132
|
+
return { status: result.status, stderr: result.stderr || "" };
|
|
133
|
+
},
|
|
134
|
+
exists: (candidate) => fs.existsSync(candidate),
|
|
135
|
+
rename: (from, to) => {
|
|
136
|
+
fs.rmSync(to, { recursive: true, force: true });
|
|
137
|
+
fs.renameSync(from, to);
|
|
138
|
+
},
|
|
139
|
+
remove: (target) => fs.rmSync(target, { recursive: true, force: true }),
|
|
140
|
+
log: (message) => console.error(message),
|
|
141
|
+
});
|
|
142
|
+
}
|
|
143
|
+
function defaultStagingRoot() {
|
|
144
|
+
return os.tmpdir();
|
|
145
|
+
}
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
import { type CodiedevConfig } from "../utils";
|
|
2
|
+
import { apiRequest } from "./shared";
|
|
3
|
+
export interface CaptureCommandDeps {
|
|
4
|
+
readConfig: () => CodiedevConfig | null;
|
|
5
|
+
writeConfig: (config: CodiedevConfig) => void;
|
|
6
|
+
apiRequest: typeof apiRequest;
|
|
7
|
+
log: (line: string) => void;
|
|
8
|
+
warn: (line: string) => void;
|
|
9
|
+
randomUUID: () => string;
|
|
10
|
+
hostname: () => string;
|
|
11
|
+
normalizePath: (value: string) => string;
|
|
12
|
+
configForRequest: (config: CodiedevConfig) => CodiedevConfig;
|
|
13
|
+
}
|
|
14
|
+
export declare function runCaptureCommand(args: string[], depsOverride?: Partial<CaptureCommandDeps>): Promise<void>;
|
|
@@ -0,0 +1,148 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
|
|
3
|
+
if (k2 === undefined) k2 = k;
|
|
4
|
+
var desc = Object.getOwnPropertyDescriptor(m, k);
|
|
5
|
+
if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
|
|
6
|
+
desc = { enumerable: true, get: function() { return m[k]; } };
|
|
7
|
+
}
|
|
8
|
+
Object.defineProperty(o, k2, desc);
|
|
9
|
+
}) : (function(o, m, k, k2) {
|
|
10
|
+
if (k2 === undefined) k2 = k;
|
|
11
|
+
o[k2] = m[k];
|
|
12
|
+
}));
|
|
13
|
+
var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
|
|
14
|
+
Object.defineProperty(o, "default", { enumerable: true, value: v });
|
|
15
|
+
}) : function(o, v) {
|
|
16
|
+
o["default"] = v;
|
|
17
|
+
});
|
|
18
|
+
var __importStar = (this && this.__importStar) || (function () {
|
|
19
|
+
var ownKeys = function(o) {
|
|
20
|
+
ownKeys = Object.getOwnPropertyNames || function (o) {
|
|
21
|
+
var ar = [];
|
|
22
|
+
for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k;
|
|
23
|
+
return ar;
|
|
24
|
+
};
|
|
25
|
+
return ownKeys(o);
|
|
26
|
+
};
|
|
27
|
+
return function (mod) {
|
|
28
|
+
if (mod && mod.__esModule) return mod;
|
|
29
|
+
var result = {};
|
|
30
|
+
if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]);
|
|
31
|
+
__setModuleDefault(result, mod);
|
|
32
|
+
return result;
|
|
33
|
+
};
|
|
34
|
+
})();
|
|
35
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
36
|
+
exports.runCaptureCommand = runCaptureCommand;
|
|
37
|
+
const crypto = __importStar(require("crypto"));
|
|
38
|
+
const os = __importStar(require("os"));
|
|
39
|
+
const captureExclusions_1 = require("../captureExclusions");
|
|
40
|
+
const utils_1 = require("../utils");
|
|
41
|
+
const shared_1 = require("./shared");
|
|
42
|
+
const DEFAULT_DEPS = {
|
|
43
|
+
readConfig: utils_1.readConfig,
|
|
44
|
+
writeConfig: utils_1.writeConfig,
|
|
45
|
+
apiRequest: shared_1.apiRequest,
|
|
46
|
+
log: console.log,
|
|
47
|
+
warn: console.warn,
|
|
48
|
+
randomUUID: () => crypto.randomUUID(),
|
|
49
|
+
hostname: () => os.hostname(),
|
|
50
|
+
normalizePath: captureExclusions_1.normalizeCapturePath,
|
|
51
|
+
configForRequest: shared_1.applyConfigEnvOverrides,
|
|
52
|
+
};
|
|
53
|
+
function requireConnectedConfig(deps) {
|
|
54
|
+
const config = deps.readConfig();
|
|
55
|
+
if (!config) {
|
|
56
|
+
throw new Error("Not connected. Run `npx codiedev connect` first.");
|
|
57
|
+
}
|
|
58
|
+
return config;
|
|
59
|
+
}
|
|
60
|
+
function withDeviceIdentity(config, deps) {
|
|
61
|
+
return {
|
|
62
|
+
...config,
|
|
63
|
+
deviceId: config.deviceId || deps.randomUUID(),
|
|
64
|
+
deviceName: config.deviceName || deps.hostname(),
|
|
65
|
+
};
|
|
66
|
+
}
|
|
67
|
+
async function syncExclusions(config, deps) {
|
|
68
|
+
try {
|
|
69
|
+
await deps.apiRequest("POST", "/api/cli/capture-exclusions", {
|
|
70
|
+
config: deps.configForRequest(config),
|
|
71
|
+
body: {
|
|
72
|
+
deviceId: config.deviceId,
|
|
73
|
+
deviceName: config.deviceName,
|
|
74
|
+
ignoredPaths: config.captureIgnoredPaths ?? [],
|
|
75
|
+
},
|
|
76
|
+
});
|
|
77
|
+
deps.log("Portal status synced.");
|
|
78
|
+
}
|
|
79
|
+
catch (error) {
|
|
80
|
+
deps.warn(`Privacy setting saved locally, but portal status did not sync: ${error.message}`);
|
|
81
|
+
}
|
|
82
|
+
}
|
|
83
|
+
function requirePath(args) {
|
|
84
|
+
const value = args.slice(1).join(" ").trim();
|
|
85
|
+
if (!value) {
|
|
86
|
+
throw new Error("Usage: codiedev ignore <path> | codiedev unignore <path>");
|
|
87
|
+
}
|
|
88
|
+
return value;
|
|
89
|
+
}
|
|
90
|
+
function reportCheck(rawPath, current, deps) {
|
|
91
|
+
const target = deps.normalizePath(rawPath);
|
|
92
|
+
const match = (0, captureExclusions_1.findMatchingCaptureExclusion)(target, current.captureIgnoredPaths);
|
|
93
|
+
if (match) {
|
|
94
|
+
deps.log(`IGNORED: ${target}`);
|
|
95
|
+
deps.log(`Matched exclusion: ${match}`);
|
|
96
|
+
}
|
|
97
|
+
else {
|
|
98
|
+
deps.log(`CAPTURED: ${target}`);
|
|
99
|
+
deps.log("No ignored-folder rule matches this path.");
|
|
100
|
+
}
|
|
101
|
+
}
|
|
102
|
+
async function runCaptureCommand(args, depsOverride = {}) {
|
|
103
|
+
const deps = { ...DEFAULT_DEPS, ...depsOverride };
|
|
104
|
+
const subcommand = args[0];
|
|
105
|
+
const current = requireConnectedConfig(deps);
|
|
106
|
+
if (subcommand === "ignore") {
|
|
107
|
+
const target = deps.normalizePath(requirePath(args));
|
|
108
|
+
const existing = (current.captureIgnoredPaths ?? []).map(deps.normalizePath);
|
|
109
|
+
const nextPaths = existing.includes(target) ? existing : [...existing, target];
|
|
110
|
+
const next = withDeviceIdentity({ ...current, captureIgnoredPaths: nextPaths }, deps);
|
|
111
|
+
deps.writeConfig(next);
|
|
112
|
+
deps.log(`Ignoring ${target} and all child folders.`);
|
|
113
|
+
await syncExclusions(next, deps);
|
|
114
|
+
return;
|
|
115
|
+
}
|
|
116
|
+
if (subcommand === "unignore") {
|
|
117
|
+
const target = deps.normalizePath(requirePath(args));
|
|
118
|
+
const existing = (current.captureIgnoredPaths ?? []).map(deps.normalizePath);
|
|
119
|
+
const next = withDeviceIdentity({ ...current, captureIgnoredPaths: existing.filter((value) => value !== target) }, deps);
|
|
120
|
+
deps.writeConfig(next);
|
|
121
|
+
deps.log(`No longer ignoring ${target}.`);
|
|
122
|
+
await syncExclusions(next, deps);
|
|
123
|
+
return;
|
|
124
|
+
}
|
|
125
|
+
if (subcommand === "ignored") {
|
|
126
|
+
// `ignored` lists everything; `ignored <path>` answers "is this ignored?".
|
|
127
|
+
const query = args.slice(1).join(" ").trim();
|
|
128
|
+
if (query) {
|
|
129
|
+
reportCheck(query, current, deps);
|
|
130
|
+
return;
|
|
131
|
+
}
|
|
132
|
+
const paths = current.captureIgnoredPaths ?? [];
|
|
133
|
+
if (paths.length === 0) {
|
|
134
|
+
deps.log("No ignored folders configured on this device.");
|
|
135
|
+
return;
|
|
136
|
+
}
|
|
137
|
+
deps.log("Ignored folders:");
|
|
138
|
+
for (const value of paths)
|
|
139
|
+
deps.log(` ${value}`);
|
|
140
|
+
return;
|
|
141
|
+
}
|
|
142
|
+
// Back-compat: the earlier `codiedev capture check <path>` form.
|
|
143
|
+
if (subcommand === "check") {
|
|
144
|
+
reportCheck(requirePath(args), current, deps);
|
|
145
|
+
return;
|
|
146
|
+
}
|
|
147
|
+
throw new Error(`Unknown command: ${subcommand ?? "(missing)"}. Use ignore, unignore, or ignored.`);
|
|
148
|
+
}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export declare function codiedevStateDir(): string;
|
|
@@ -0,0 +1,54 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
|
|
3
|
+
if (k2 === undefined) k2 = k;
|
|
4
|
+
var desc = Object.getOwnPropertyDescriptor(m, k);
|
|
5
|
+
if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
|
|
6
|
+
desc = { enumerable: true, get: function() { return m[k]; } };
|
|
7
|
+
}
|
|
8
|
+
Object.defineProperty(o, k2, desc);
|
|
9
|
+
}) : (function(o, m, k, k2) {
|
|
10
|
+
if (k2 === undefined) k2 = k;
|
|
11
|
+
o[k2] = m[k];
|
|
12
|
+
}));
|
|
13
|
+
var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
|
|
14
|
+
Object.defineProperty(o, "default", { enumerable: true, value: v });
|
|
15
|
+
}) : function(o, v) {
|
|
16
|
+
o["default"] = v;
|
|
17
|
+
});
|
|
18
|
+
var __importStar = (this && this.__importStar) || (function () {
|
|
19
|
+
var ownKeys = function(o) {
|
|
20
|
+
ownKeys = Object.getOwnPropertyNames || function (o) {
|
|
21
|
+
var ar = [];
|
|
22
|
+
for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k;
|
|
23
|
+
return ar;
|
|
24
|
+
};
|
|
25
|
+
return ownKeys(o);
|
|
26
|
+
};
|
|
27
|
+
return function (mod) {
|
|
28
|
+
if (mod && mod.__esModule) return mod;
|
|
29
|
+
var result = {};
|
|
30
|
+
if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]);
|
|
31
|
+
__setModuleDefault(result, mod);
|
|
32
|
+
return result;
|
|
33
|
+
};
|
|
34
|
+
})();
|
|
35
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
36
|
+
exports.codiedevStateDir = codiedevStateDir;
|
|
37
|
+
// Single source of truth for where the CLI's config/state lives on disk.
|
|
38
|
+
//
|
|
39
|
+
// Defaults to ~/.codiedev. The desktop app sets CODIEDEV_CONFIG_DIR to
|
|
40
|
+
// ~/.codiedev-desktop so a desktop session never touches the user's real
|
|
41
|
+
// prod config — env-based (not a flag) so detached capture-finish/upload
|
|
42
|
+
// children inherit the override automatically.
|
|
43
|
+
//
|
|
44
|
+
// Deliberately dependency-free (only node:path/os) so it can be imported
|
|
45
|
+
// by both utils.ts and modules utils.ts itself imports (e.g. captureLog.ts)
|
|
46
|
+
// without creating a circular import.
|
|
47
|
+
const path = __importStar(require("path"));
|
|
48
|
+
const os = __importStar(require("os"));
|
|
49
|
+
const CONFIG_DIR = process.env.CODIEDEV_CONFIG_DIR?.trim()
|
|
50
|
+
? path.resolve(process.env.CODIEDEV_CONFIG_DIR.trim())
|
|
51
|
+
: path.join(os.homedir(), ".codiedev");
|
|
52
|
+
function codiedevStateDir() {
|
|
53
|
+
return CONFIG_DIR;
|
|
54
|
+
}
|
package/dist/connect.js
CHANGED
|
@@ -37,6 +37,8 @@ exports.runConnect = runConnect;
|
|
|
37
37
|
const readline = __importStar(require("readline"));
|
|
38
38
|
const https = __importStar(require("https"));
|
|
39
39
|
const http = __importStar(require("http"));
|
|
40
|
+
const crypto = __importStar(require("crypto"));
|
|
41
|
+
const os = __importStar(require("os"));
|
|
40
42
|
const utils_1 = require("./utils");
|
|
41
43
|
const connectFlow_1 = require("./connectFlow");
|
|
42
44
|
const version_1 = require("./version");
|
|
@@ -210,6 +212,9 @@ async function runConnect(args = []) {
|
|
|
210
212
|
repos,
|
|
211
213
|
lastSynced: new Date().toISOString(),
|
|
212
214
|
backendUrl: BACKEND_URL,
|
|
215
|
+
deviceId: saved?.deviceId || crypto.randomUUID(),
|
|
216
|
+
deviceName: saved?.deviceName || os.hostname(),
|
|
217
|
+
captureIgnoredPaths: saved?.captureIgnoredPaths ?? [],
|
|
213
218
|
};
|
|
214
219
|
try {
|
|
215
220
|
(0, utils_1.writeConfig)(config);
|
|
@@ -223,6 +228,20 @@ async function runConnect(args = []) {
|
|
|
223
228
|
// a timeline when a customer says "it connected but it doesn't work."
|
|
224
229
|
// Fire-and-forget — never blocks the connect flow.
|
|
225
230
|
(0, shared_1.recordInstallEvent)(config, { step: "started", ok: true });
|
|
231
|
+
// One-way device status sync. Local config remains authoritative: a network
|
|
232
|
+
// failure here never removes or weakens an ignored path. This also registers
|
|
233
|
+
// a freshly connected device with an empty list so the portal can distinguish
|
|
234
|
+
// "connected, no exclusions" from "old CLI has never reported status".
|
|
235
|
+
try {
|
|
236
|
+
await postJson(`${BACKEND_URL}/api/cli/capture-exclusions`, {
|
|
237
|
+
deviceId: config.deviceId,
|
|
238
|
+
deviceName: config.deviceName,
|
|
239
|
+
ignoredPaths: config.captureIgnoredPaths,
|
|
240
|
+
}, { Authorization: `Bearer ${token}` });
|
|
241
|
+
}
|
|
242
|
+
catch (err) {
|
|
243
|
+
console.warn(`Warning: Local capture settings are active, but portal status did not sync — ${err.message}`);
|
|
244
|
+
}
|
|
226
245
|
// Migration: strip MCP server entries written by pre-CLI-only versions
|
|
227
246
|
// of `codiedev connect`. These pointed at `npx codiedev-mcp`, a binary
|
|
228
247
|
// that was removed in 0.3.4 and that 0.7.11+ no longer ships. Without
|
|
@@ -251,9 +270,11 @@ async function runConnect(args = []) {
|
|
|
251
270
|
const hasClaude = (0, utils_1.claudeCodeInstalled)();
|
|
252
271
|
const hasCodex = (0, utils_1.codexInstalled)();
|
|
253
272
|
const hasCursor = (0, utils_1.cursorInstalled)();
|
|
273
|
+
const hasOpenCode = (0, utils_1.openCodeInstalled)();
|
|
254
274
|
(0, shared_1.recordInstallEvent)(config, { step: "detect", target: "claude-code", ok: hasClaude });
|
|
255
275
|
(0, shared_1.recordInstallEvent)(config, { step: "detect", target: "codex", ok: hasCodex });
|
|
256
276
|
(0, shared_1.recordInstallEvent)(config, { step: "detect", target: "cursor", ok: hasCursor });
|
|
277
|
+
(0, shared_1.recordInstallEvent)(config, { step: "detect", target: "opencode", ok: hasOpenCode });
|
|
257
278
|
const installed = [];
|
|
258
279
|
// Machine-readable target keys reported back to the backend via
|
|
259
280
|
// /api/cli/markInstalled so the portal's "Connected" state reflects
|
|
@@ -292,6 +313,10 @@ async function runConnect(args = []) {
|
|
|
292
313
|
runInstall("cursor-hook", utils_1.installCursorHook, "Cursor sessionEnd (capture + auto-backup) hooks (~/.cursor/hooks.json)", "cursor-hook");
|
|
293
314
|
runInstall("cursor-instructions", utils_1.installCursorInstructions, "Cursor agent instructions (~/.cursor/rules/codiedev.mdc)", "cursor-instructions");
|
|
294
315
|
}
|
|
316
|
+
// Always stage the adapter. It is dormant until OpenCode starts, which lets
|
|
317
|
+
// a user connect Claude Code today and install the Codie harness tomorrow
|
|
318
|
+
// without needing a second setup ritual.
|
|
319
|
+
runInstall("opencode-capture-adapter", utils_1.installOpenCodeCapturePlugin, "OpenCode session capture adapter (~/.config/opencode/plugins/codiedev.js)", "opencode-capture");
|
|
295
320
|
// Tell the backend which targets we wired up so the portal's onboarding
|
|
296
321
|
// card flips to "Connected" only when something actually installed —
|
|
297
322
|
// not on bare token validation. Best-effort: a network failure here
|
|
@@ -314,8 +339,8 @@ async function runConnect(args = []) {
|
|
|
314
339
|
});
|
|
315
340
|
}
|
|
316
341
|
}
|
|
317
|
-
if (!hasClaude && !hasCodex && !hasCursor) {
|
|
318
|
-
console.warn("\nNo
|
|
342
|
+
if (!hasClaude && !hasCodex && !hasCursor && !hasOpenCode) {
|
|
343
|
+
console.warn("\nNo existing AI client detected; the Codie/OpenCode adapter is ready for a later install.");
|
|
319
344
|
console.warn("If you just installed Claude Code, launch it once (so ~/.claude is created),");
|
|
320
345
|
console.warn("then re-run `npx codiedev connect` to wire up the capture hook.");
|
|
321
346
|
}
|