@awak-app/simy-cli 0.7.3 → 0.7.4
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 +9 -1
- package/package.json +1 -1
- package/src/cli-contract.js +1 -1
- package/src/desktop-host.js +7 -3
- package/src/desktop-status.js +11 -1
- package/src/doctor.js +11 -1
package/README.md
CHANGED
|
@@ -15,7 +15,7 @@ cached in memory, so restarting the host may retrieve live contexts again.
|
|
|
15
15
|
|
|
16
16
|
`simy --version` prints the installed CLI version without starting an agent or
|
|
17
17
|
checking for updates. `simy doctor` performs read-only local connection and
|
|
18
|
-
provider login checks and prints a JSON diagnostic report: installed/running
|
|
18
|
+
provider login and SIMY provider-link checks and prints a JSON diagnostic report: installed/running
|
|
19
19
|
versions, stale discovery records, session expiry, provider readiness, and recent
|
|
20
20
|
structured SQM failures, plus `host_exit` with the latest native desktop host's
|
|
21
21
|
abnormal exit time and status. `failures.total_failures` counts scoped SQM
|
|
@@ -1095,3 +1095,11 @@ Diagnosis now returns an issue-first worksheet in `analysis.hypothesisSheet.requ
|
|
|
1095
1095
|
Default issue selection is the first eight original acceptance criteria (or `objective` when absent); `omittedIssueCount` and `issueDescriptionsTruncated` disclose bounded input. Use an explicit issue list to focus on other original issues, including a prior `rule:*` or `objective` row. The latest retained runtime worksheet is supplied as context; this read-only command neither writes nor replaces the runtime ledger.
|
|
1096
1096
|
|
|
1097
1097
|
Manual diagnosis and delivery reasoning share the scoped SQM knowledge loader. Partial environment overrides cannot mix a token, account, organization or endpoint with saved credentials; unavailable knowledge is reported without blocking the diagnosis. The brief response retains `knowledge.status`, `reason`, `suppliedCount`, and `bundleDigest`, separately from `knowledgeReferenceCount` and test execution (always zero for diagnosis). Truncated evidence is disclosed to both the analyst and the caller.
|
|
1098
|
+
|
|
1099
|
+
Provider diagnostics distinguish `authentication`, `connection`, and `connection_ready`.
|
|
1100
|
+
If both login and SIMY connection are missing, `actions` lists both remedies in one
|
|
1101
|
+
report. Connection readiness does not certify model access or task completion.
|
|
1102
|
+
Diagnostics do not reconnect providers or enable existing tasks. Explicit refresh
|
|
1103
|
+
waits for a fresh probe when a pre-login check is already running. macOS login
|
|
1104
|
+
terminals preserve an explicitly configured `CODEX_HOME` or `CLAUDE_CONFIG_DIR`,
|
|
1105
|
+
without copying credentials.
|
package/package.json
CHANGED
package/src/cli-contract.js
CHANGED
|
@@ -13,7 +13,7 @@ import {
|
|
|
13
13
|
} from "./durable-local-task-steps-contract.js";
|
|
14
14
|
import { buildExecutionCapabilityContract } from "./execution-capability-contract.js";
|
|
15
15
|
|
|
16
|
-
export const CLI_VERSION = "0.7.
|
|
16
|
+
export const CLI_VERSION = "0.7.4";
|
|
17
17
|
export const CLI_API_CONTRACT_VERSION = 10;
|
|
18
18
|
|
|
19
19
|
export function narrativeImageCapabilityMetadata() {
|
package/src/desktop-host.js
CHANGED
|
@@ -39,7 +39,7 @@ import { createCliAutoUpdater, readCliPackageInfo } from "./auto-update.js";
|
|
|
39
39
|
import { createAgentShutdown } from "./shutdown.js";
|
|
40
40
|
import { resolveWebOrigin } from "./web-origin.js";
|
|
41
41
|
import { openAuthorizationUrl } from "./browser.js";
|
|
42
|
-
import { inspectBackendExecutable } from "./backend-executable.js";
|
|
42
|
+
import { inspectBackendExecutable, CODEX_WRITER_LOCK_MINIMUM_VERSION } from "./backend-executable.js";
|
|
43
43
|
import { inspectSqmAgentSkill } from "./sqm/agent-skill.js";
|
|
44
44
|
import { startWorkflowUpdates, syncInstalledWorkflows } from "./sqm/workflow-update.js";
|
|
45
45
|
import { createNativeDevelopmentReportsApi, normalizeNativeDevelopmentReportSave } from "./native-development-reports.js";
|
|
@@ -1442,7 +1442,7 @@ export async function openProviderLogin(provider, root, {
|
|
|
1442
1442
|
execFileImpl = promisify(execFile), platform = process.platform, env = process.env,
|
|
1443
1443
|
} = {}) {
|
|
1444
1444
|
if (provider !== "codex" && provider !== "claude") throw new Error("Unsupported provider.");
|
|
1445
|
-
const executable = await inspectExecutable(provider);
|
|
1445
|
+
const executable = await inspectExecutable(provider, provider === "codex" ? { minimumVersion: CODEX_WRITER_LOCK_MINIMUM_VERSION } : {});
|
|
1446
1446
|
if (!executable.available || !executable.executable) throw Object.assign(new Error("Provider is not installed."), { code: "provider_not_installed" });
|
|
1447
1447
|
const label = provider === "claude" ? "Claude Code" : "Codex";
|
|
1448
1448
|
const args = provider === "claude" ? ["auth", "login"] : ["login"];
|
|
@@ -1456,7 +1456,11 @@ export async function openProviderLogin(provider, root, {
|
|
|
1456
1456
|
const directory = await mkdtemp(join(root, `${provider}-login-`));
|
|
1457
1457
|
const script = join(directory, `Sign in to ${label}.command`);
|
|
1458
1458
|
const quote = (s) => `'${s.replaceAll("'", "'\\''")}'`;
|
|
1459
|
-
|
|
1459
|
+
// Terminal does not inherit custom provider homes from the desktop host.
|
|
1460
|
+
// Preserve only path settings, never copy tokens or credential contents.
|
|
1461
|
+
const homeKey = provider === "codex" ? "CODEX_HOME" : "CLAUDE_CONFIG_DIR";
|
|
1462
|
+
const providerHome = typeof env[homeKey] === "string" && env[homeKey] ? `export ${homeKey}=${quote(env[homeKey])}\n` : "";
|
|
1463
|
+
await writeFile(script, `#!/bin/bash\n${providerHome}export PATH=${quote(env.PATH || "/usr/bin:/bin")}\n${quote(executable.executable)} ${args.join(" ")}\nresult=$?\nif [ "$result" -ne 0 ]; then\n echo "Sign-in failed. Check the error above, then press Return to close."\n read -r _\nfi\nrm -f -- ${quote(script)}\nrmdir -- ${quote(directory)}\nexit "$result"\n`, { mode: 0o700 });
|
|
1460
1464
|
await chmod(script, 0o700);
|
|
1461
1465
|
try { await execFileImpl("/usr/bin/open", ["-a", "Terminal", script], { timeout: 10_000 }); }
|
|
1462
1466
|
catch (error) { await rm(directory, { recursive: true, force: true }); throw error; }
|
package/src/desktop-status.js
CHANGED
|
@@ -137,9 +137,19 @@ export function createProviderStatusInspector(provider, {
|
|
|
137
137
|
let cached = null;
|
|
138
138
|
let cachedAt = 0;
|
|
139
139
|
let inFlight = null;
|
|
140
|
-
|
|
140
|
+
const refreshAfterFlight = new WeakMap();
|
|
141
|
+
return async function readStatus({ force = false } = {}) {
|
|
141
142
|
const current = now();
|
|
142
143
|
if (!force && cached && current - cachedAt < cacheMs) return structuredClone(cached);
|
|
144
|
+
if (inFlight && force) {
|
|
145
|
+
// A login can finish while a previous signed-out probe is in flight.
|
|
146
|
+
// Explicit refresh must observe after that probe, not return its old result.
|
|
147
|
+
const previous = inFlight;
|
|
148
|
+
if (!refreshAfterFlight.has(previous)) {
|
|
149
|
+
refreshAfterFlight.set(previous, previous.catch(() => {}).then(() => readStatus({ force: true })));
|
|
150
|
+
}
|
|
151
|
+
return structuredClone(await refreshAfterFlight.get(previous));
|
|
152
|
+
}
|
|
143
153
|
if (inFlight) return structuredClone(await inFlight);
|
|
144
154
|
inFlight = Promise.resolve(inspect({ ...inspectOptions, now: () => new Date(now()) }))
|
|
145
155
|
.then((result) => {
|
package/src/doctor.js
CHANGED
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import { readProviderLinks } from "./provider-links.js";
|
|
1
2
|
import { purposeSigningKeyPath, purposeVerificationCapability } from "./purpose-local-proof.js";
|
|
2
3
|
import { readFile } from "node:fs/promises";
|
|
3
4
|
import { connectionDiscoveryPath } from "./connection-discovery.js";
|
|
@@ -7,7 +8,7 @@ import { readDiagnosticLog } from "./diagnostic-log.js";
|
|
|
7
8
|
import { readHostExitDiagnostics } from "./host-exit-diagnostics.js";
|
|
8
9
|
|
|
9
10
|
export async function collectDiagnostics({ webOrigin, cliVersion, root = defaultSessionRoot(),
|
|
10
|
-
readSessionImpl = readSession, inspectProvider = inspectProviderStatus, fetchImpl = fetch,
|
|
11
|
+
readSessionImpl = readSession, inspectProvider = inspectProviderStatus, readLinks = readProviderLinks, fetchImpl = fetch,
|
|
11
12
|
readFileImpl = readFile, alive = pid => { try { process.kill(pid, 0); return true; } catch { return false; } }, now = Date.now() } = {}) {
|
|
12
13
|
const session = await readSessionImpl(webOrigin, root).catch(() => null);
|
|
13
14
|
const valid = isSessionValid(session, now, webOrigin);
|
|
@@ -27,10 +28,14 @@ export async function collectDiagnostics({ webOrigin, cliVersion, root = default
|
|
|
27
28
|
background: health.daemon === true };
|
|
28
29
|
}
|
|
29
30
|
} catch (error) { if (error.code !== "ENOENT") daemon.state = "unverified"; }
|
|
31
|
+
const links = await readLinks(root).catch(() => ({}));
|
|
30
32
|
const providers = Object.fromEntries(await Promise.all(["codex", "claude"].map(async provider => {
|
|
31
33
|
const result = await inspectProvider(provider).catch(() => ({ state: "unknown" }));
|
|
32
34
|
return [provider, { state: ["ready", "signed_out", "missing", "outdated", "unknown"].includes(result?.state) ? result.state : "unknown",
|
|
33
35
|
version: typeof result?.version === "string" && /^\d+\.\d+\.\d+(?:-[a-z0-9.-]+)?$/i.test(result.version) ? result.version : null,
|
|
36
|
+
connection: links[provider]?.settingsError || !links[provider] ? "unknown" : links[provider].connected ? "connected" : "disconnected",
|
|
37
|
+
authentication: result?.state === "ready" ? "signed_in" : result?.state === "signed_out" ? "signed_out" : "unknown",
|
|
38
|
+
connection_ready: result?.state === "ready" && links[provider]?.connected === true && !links[provider]?.settingsError,
|
|
34
39
|
model_access: "unknown" }];
|
|
35
40
|
})));
|
|
36
41
|
const scope = session?.account_id && session?.organization_id ? { webOrigin, accountId: session.account_id, organizationId: session.organization_id } : null;
|
|
@@ -41,6 +46,11 @@ export async function collectDiagnostics({ webOrigin, cliVersion, root = default
|
|
|
41
46
|
providers, failures, host_exit: await readHostExitDiagnostics(root),
|
|
42
47
|
purpose_verification: await purposeVerificationCapability(purposeSigningKeyPath(root)).then(value => ({ state: value ? "available" : "not_initialized", key_id: value?.key_id || null, execution: "explicit_frozen_recipes_only" })).catch(() => ({ state: "unavailable", key_id: null, execution: "explicit_frozen_recipes_only" })),
|
|
43
48
|
actions: [
|
|
49
|
+
...Object.entries(providers).filter(([, provider]) => provider.connection === "disconnected")
|
|
50
|
+
.map(([provider]) => ({ id: `connect_${provider}`, action: "provider_connect", provider,
|
|
51
|
+
instruction: `Open SIMY settings and connect ${provider}. Provider login and SIMY connection are separate; existing tasks remain stopped.` })),
|
|
52
|
+
...Object.entries(providers).filter(([, provider]) => provider.connection === "unknown")
|
|
53
|
+
.map(([provider]) => ({ id: `check_${provider}_connection`, instruction: "SIMY connection settings could not be read. Refresh connections; do not reset saved consent." })),
|
|
44
54
|
...(!valid ? [{ id: "reconnect_cli", command: "simy" }] : []),
|
|
45
55
|
...(["stale_record", "not_found", "unverified"].includes(daemon.state) ? [{ id: "inspect_daemon", command: "simy doctor" }] : []),
|
|
46
56
|
...(daemon.version_matches === false ? [{ id: "update_when_idle", command: "simy update --check" }] : []),
|