@getmikie/cli 1.1.86 → 1.1.88

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 CHANGED
@@ -16,3 +16,18 @@ mikie mcp
16
16
  The launcher contains no relay, pricing, receipt, or model logic. npm installs
17
17
  the matching `@getmikie/*` optional platform package and the launcher executes that
18
18
  binary with inherited stdio.
19
+
20
+ ## Mikie Control
21
+
22
+ Run one immutable, repository-scoped request through the verified Codex App
23
+ Server runtime:
24
+
25
+ ```sh
26
+ mikie control run --request C:\absolute\path\control-request.json --json
27
+ ```
28
+
29
+ The request must be an existing absolute JSON file. Mikie validates the frozen
30
+ repository, allowed paths, deterministic verifier, exact model and reasoning
31
+ effort, bounded model-turn timeout, and private temporary root before execution. The JSON result is
32
+ content-free: it reports terminal status and hashes, never prompts, context,
33
+ patch bodies, verifier output, or credentials.
package/bin/mikie.mjs CHANGED
@@ -1,12 +1,28 @@
1
1
  #!/usr/bin/env node
2
2
 
3
3
  import { createRequire } from "node:module";
4
+ import { homedir } from "node:os";
5
+ import { access } from "node:fs/promises";
6
+ import path from "node:path";
7
+ import { fileURLToPath } from "node:url";
4
8
 
5
9
  import {
10
+ launchNativeCaptured,
6
11
  launchNative,
7
12
  resolveNativeBinary,
13
+ selectPlatformPackage,
8
14
  translateArguments,
9
15
  } from "../lib/launcher.mjs";
16
+ import {
17
+ UpdateTopology,
18
+ createCommandAdapter,
19
+ createFilesystemAdapter,
20
+ createRegistryAdapter,
21
+ detectUpdateTopology,
22
+ externalRecoveryInvocation,
23
+ parseUpdateHandoff,
24
+ runUpdateTransaction,
25
+ } from "../lib/update.mjs";
10
26
 
11
27
  const require = createRequire(import.meta.url);
12
28
  const packageMetadata = require("../package.json");
@@ -22,9 +38,150 @@ Usage:
22
38
  mikie mcp [--adapter codex|claude_code]
23
39
  mikie login
24
40
  mikie connect .
41
+ mikie control run --request <absolute-json> --json
25
42
  mikie doctor
43
+ mikie update
26
44
 
27
45
  All commands execute the compiled Mikie native engine.`);
46
+ } else if (arguments_.length === 1 && arguments_[0].toLowerCase() === "update") {
47
+ try {
48
+ const commands = createCommandAdapter();
49
+ const npmRoot = await commands.run("npm", ["root", "--global"], {
50
+ shell: false,
51
+ capture: true,
52
+ });
53
+ const npmPrefix = await commands.run("npm", ["prefix", "--global"], {
54
+ shell: false,
55
+ capture: true,
56
+ });
57
+ if (
58
+ npmRoot.code !== 0 ||
59
+ npmRoot.stdout.trim().split(/\r?\n/).length !== 1 ||
60
+ !path.isAbsolute(npmRoot.stdout.trim()) ||
61
+ npmPrefix.code !== 0 ||
62
+ npmPrefix.stdout.trim().split(/\r?\n/).length !== 1 ||
63
+ !path.isAbsolute(npmPrefix.stdout.trim())
64
+ ) {
65
+ throw new Error("global npm installation authority is unavailable");
66
+ }
67
+ const topology = detectUpdateTopology({ globalNodeModules: npmRoot.stdout.trim() });
68
+ const customerHome = homedir();
69
+ const transactionRoot = path.join(customerHome, ".mikie", "npm-update-v1");
70
+ const recoveryJournal = path.join(transactionRoot, "journal.json");
71
+ if (topology === UpdateTopology.NpmGlobal) {
72
+ try {
73
+ await access(recoveryJournal);
74
+ const invocation = await externalRecoveryInvocation(transactionRoot);
75
+ const recovered = await commands.run(invocation.executable, invocation.args, {
76
+ shell: false,
77
+ capture: true,
78
+ });
79
+ if (recovered.code !== 0) {
80
+ throw new Error("pending Mikie update recovery failed");
81
+ }
82
+ } catch (error) {
83
+ if (error?.code !== "ENOENT") throw error;
84
+ }
85
+ }
86
+ const selected = selectPlatformPackage();
87
+ const platformPackageJson = require.resolve(`${selected.packageName}/package.json`);
88
+ const wrapperPackageJson = fileURLToPath(new URL("../package.json", import.meta.url));
89
+ const binary = await resolveNativeBinary();
90
+ const globalPrefix = npmPrefix.stdout.trim();
91
+ const globalShims =
92
+ process.platform === "win32"
93
+ ? ["mikie", "mikie.cmd", "mikie.ps1"].map((name) => path.join(globalPrefix, name))
94
+ : [path.join(globalPrefix, "bin", "mikie")];
95
+ const handoffResult = await launchNativeCaptured(binary, ["update"], { topology });
96
+ if (handoffResult.code !== 0) {
97
+ if (handoffResult.stderr) process.stderr.write(handoffResult.stderr);
98
+ throw new Error(`native update handoff exited with status ${handoffResult.code}`);
99
+ }
100
+ const handoff = parseUpdateHandoff(handoffResult.stdout, topology);
101
+ const registry = createRegistryAdapter({ commands });
102
+ const files = createFilesystemAdapter({
103
+ currentLayout: { wrapperPackageJson, platformPackageJson, binary, shims: globalShims },
104
+ locateCandidate: async ({ stagingPrefix } = {}) => {
105
+ if (stagingPrefix) {
106
+ const nodeModules =
107
+ process.platform === "win32"
108
+ ? path.join(stagingPrefix, "node_modules")
109
+ : path.join(stagingPrefix, "lib", "node_modules");
110
+ const stagedWrapper = path.join(nodeModules, "@getmikie", "cli", "package.json");
111
+ const stagedPlatform = path.join(
112
+ nodeModules,
113
+ ...selected.packageName.split("/"),
114
+ "package.json",
115
+ );
116
+ return {
117
+ wrapperPackageJson: stagedWrapper,
118
+ platformPackageJson: stagedPlatform,
119
+ binary: path.join(
120
+ path.dirname(stagedPlatform),
121
+ ...selected.binaryPath.split("/"),
122
+ ),
123
+ shims:
124
+ process.platform === "win32"
125
+ ? ["mikie", "mikie.cmd", "mikie.ps1"].map((name) =>
126
+ path.join(stagingPrefix, name),
127
+ )
128
+ : [path.join(stagingPrefix, "bin", "mikie")],
129
+ };
130
+ }
131
+ return {
132
+ wrapperPackageJson,
133
+ platformPackageJson: require.resolve(`${selected.packageName}/package.json`),
134
+ binary: await resolveNativeBinary(),
135
+ shims: globalShims,
136
+ };
137
+ },
138
+ providerPaths: {
139
+ registration: path.join(customerHome, ".claude.json"),
140
+ settings: path.join(customerHome, ".claude", "settings.json"),
141
+ ownership: path.join(customerHome, ".claude", "mikie-evidence-hook-ownership.json"),
142
+ },
143
+ transactionRoot,
144
+ settleProviderRepair: async (phase, snapshot) => {
145
+ const settlementBinary = snapshot?.preparedCandidate?.binary;
146
+ const finalLauncher = snapshot?.preparedCandidate?.finalLauncher;
147
+ if (!settlementBinary || !finalLauncher) {
148
+ throw new Error("prepared candidate recovery authority is unavailable");
149
+ }
150
+ const settlement = await commands.run(settlementBinary, handoff.repair_command, {
151
+ shell: false,
152
+ capture: true,
153
+ environment: {
154
+ MIKIE_DISTRIBUTION_MODE: "npm",
155
+ MIKIE_NPM_UPDATE_TOPOLOGY: UpdateTopology.NpmGlobal,
156
+ MIKIE_NPM_UPDATE_REPAIR_PHASE: phase,
157
+ MIKIE_UPDATE_REPAIR_CANDIDATE: settlementBinary,
158
+ MIKIE_UPDATE_REPAIR_LAUNCHER: finalLauncher,
159
+ },
160
+ });
161
+ if (settlement.code !== 0) {
162
+ throw new Error("candidate owned-adapter settlement failed");
163
+ }
164
+ },
165
+ });
166
+ const result = await runUpdateTransaction({
167
+ handoff,
168
+ currentVersion: packageMetadata.version,
169
+ platformPackage: selected.packageName,
170
+ registry,
171
+ commands,
172
+ files,
173
+ });
174
+ if (result.status === "updated") {
175
+ console.log(`Updated Mikie to ${result.version}.`);
176
+ for (const setting of result.foreignSettings) {
177
+ console.log(`Preserved foreign setting: ${setting}`);
178
+ }
179
+ }
180
+ } catch (error) {
181
+ const message = error instanceof Error ? error.message : "Mikie update failed";
182
+ console.error(`mikie: ${message}`);
183
+ process.exitCode = 1;
184
+ }
28
185
  } else {
29
186
  try {
30
187
  const binary = await resolveNativeBinary();
package/lib/launcher.mjs CHANGED
@@ -17,6 +17,8 @@ const SIGNAL_EXIT_CODES = new Map([
17
17
  ["SIGTERM", 143],
18
18
  ]);
19
19
 
20
+ const MAX_CAPTURED_OUTPUT_BYTES = 1024 * 1024;
21
+
20
22
  function currentRuntimeUsesGlibc() {
21
23
  try {
22
24
  const report = process.report?.getReport();
@@ -122,3 +124,89 @@ export function launchNative(
122
124
  });
123
125
  });
124
126
  }
127
+
128
+ export function launchNativeCaptured(
129
+ binary,
130
+ arguments_,
131
+ {
132
+ topology,
133
+ spawnProcess = spawn,
134
+ environment = process.env,
135
+ timeoutMs = 30_000,
136
+ scheduleTimeout = setTimeout,
137
+ cancelTimeout = clearTimeout,
138
+ } = {},
139
+ ) {
140
+ if (!["npm_global", "npm_ephemeral"].includes(topology)) {
141
+ throw new Error("Mikie npm update topology is invalid");
142
+ }
143
+ if (!Number.isInteger(timeoutMs) || timeoutMs <= 0) {
144
+ throw new Error("Mikie native update timeout is invalid");
145
+ }
146
+ return new Promise((resolve, reject) => {
147
+ let settled = false;
148
+ const stdout = [];
149
+ const stderr = [];
150
+ let capturedBytes = 0;
151
+ const child = spawnProcess(binary, arguments_, {
152
+ shell: false,
153
+ stdio: ["inherit", "pipe", "pipe"],
154
+ windowsHide: true,
155
+ env: {
156
+ ...environment,
157
+ MIKIE_DISTRIBUTION_MODE: "npm",
158
+ MIKIE_NPM_UPDATE_TOPOLOGY: topology,
159
+ },
160
+ });
161
+ const deadline = scheduleTimeout(() => {
162
+ if (settled) return;
163
+ settled = true;
164
+ try {
165
+ child.kill?.("SIGKILL");
166
+ } catch {
167
+ // The child may already have exited.
168
+ }
169
+ reject(new Error("Mikie native update command timed out"));
170
+ }, timeoutMs);
171
+ const finish = (callback) => {
172
+ if (settled) return false;
173
+ settled = true;
174
+ cancelTimeout(deadline);
175
+ callback();
176
+ return true;
177
+ };
178
+ const capture = (target) => (chunk) => {
179
+ if (settled) return;
180
+ const bytes = Buffer.isBuffer(chunk) ? chunk : Buffer.from(chunk);
181
+ capturedBytes += bytes.length;
182
+ if (capturedBytes > MAX_CAPTURED_OUTPUT_BYTES) {
183
+ finish(() => {
184
+ try {
185
+ child.kill?.("SIGKILL");
186
+ } catch {
187
+ // The child may already have exited.
188
+ }
189
+ reject(new Error("Mikie native update response exceeded the capture limit"));
190
+ });
191
+ return;
192
+ }
193
+ target.push(bytes);
194
+ };
195
+ child.stdout.on("data", capture(stdout));
196
+ child.stderr.on("data", capture(stderr));
197
+ child.once("error", (error) => {
198
+ finish(() =>
199
+ reject(new Error("Mikie native update command could not be started", { cause: error })),
200
+ );
201
+ });
202
+ child.once("close", (code, signal) => {
203
+ finish(() =>
204
+ resolve({
205
+ code: Number.isInteger(code) ? code : (SIGNAL_EXIT_CODES.get(signal) ?? 1),
206
+ stdout: Buffer.concat(stdout).toString("utf8"),
207
+ stderr: Buffer.concat(stderr).toString("utf8"),
208
+ }),
209
+ );
210
+ });
211
+ });
212
+ }