@indigoai-us/hq-cli 5.345.121 → 5.345.123
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/CHANGELOG.md +22 -0
- package/README.md +15 -0
- package/assets/scaffold/core/scripts/handoff-sync-prefetch.sh +121 -0
- package/assets/scaffold/core/scripts/handoff-sync-publish.sh +124 -0
- package/assets/scaffold/core/scripts/knowledge-pulse-lock.sh +27 -0
- package/assets/scaffold/core/scripts/lib/session-skill-catalog.sh +226 -0
- package/assets/scaffold/core/scripts/skill-installed.sh +43 -0
- package/dist/command-catalog.generated.d.ts +97 -0
- package/dist/command-catalog.generated.js +116 -0
- package/dist/command-registration-plan.d.ts +6 -0
- package/dist/command-registration-plan.js +1 -0
- package/dist/commands/core-bucket-c-handoff-sync-fast.d.ts +3 -0
- package/dist/commands/core-bucket-c-handoff-sync-fast.js +16 -0
- package/dist/commands/core-bucket-c-handoff-sync-native.d.ts +45 -0
- package/dist/commands/core-bucket-c-handoff-sync-native.js +300 -0
- package/dist/commands/core-bucket-c-small-fast.d.ts +5 -0
- package/dist/commands/core-bucket-c-small-fast.js +19 -0
- package/dist/commands/core-bucket-c-small-native.d.ts +11 -0
- package/dist/commands/core-bucket-c-small-native.js +197 -0
- package/dist/commands/core-derive-trigger-facts-native.js +4 -1
- package/dist/commands/core.js +25 -0
- package/dist/commands/deploy-comments.d.ts +2 -0
- package/dist/commands/deploy-comments.js +2 -2
- package/dist/commands/deploy-list.d.ts +22 -0
- package/dist/commands/deploy-list.js +153 -0
- package/dist/commands/lanes.js +50 -3
- package/dist/fast-scaffold.js +48 -0
- package/dist/lib/capacity/collectors/claude-oauth.d.ts +1 -1
- package/dist/lib/capacity/collectors/claude-oauth.js +48 -4
- package/dist/lib/capacity/ledger.d.ts +7 -0
- package/dist/lib/capacity/ledger.js +17 -1
- package/dist/lib/capacity/sign-in.d.ts +2 -0
- package/dist/lib/capacity/sign-in.js +19 -4
- package/dist/lib/core-utils/session-company.d.ts +3 -0
- package/dist/lib/core-utils/session-company.js +78 -0
- package/dist/lib/lanes/spawn.d.ts +1 -0
- package/dist/lib/lanes/spawn.js +4 -1
- package/dist/lib/lanes/store.js +11 -0
- package/dist/lib/lanes/types.d.ts +2 -0
- package/dist/lib/lanes/types.js +1 -1
- package/package.json +1 -1
|
@@ -1687,6 +1687,37 @@ const defaultWriteClaudeCredentials = async (request) => {
|
|
|
1687
1687
|
}
|
|
1688
1688
|
};
|
|
1689
1689
|
const HQ_CLI_PACKAGE_NAME = "@indigoai-us/hq-cli";
|
|
1690
|
+
const LOADER_FLAGS_WITH_VALUE = new Set(["--import", "--loader", "--experimental-loader", "--require", "-r", "--conditions", "-C"]);
|
|
1691
|
+
const LOADER_FLAGS_BARE = new Set(["--experimental-strip-types", "--experimental-transform-types"]);
|
|
1692
|
+
/**
|
|
1693
|
+
* Node flags that load a TypeScript runner (tsx and similar). A dev run of the TS
|
|
1694
|
+
* source needs them again in the child or `node src/index.ts` fails; compiled `.js`
|
|
1695
|
+
* entries get none. Other exec flags (`--inspect`, memory limits) are not copied.
|
|
1696
|
+
*/
|
|
1697
|
+
function loaderArgsForSource(entry, execArgv) {
|
|
1698
|
+
if (!/\.(?:[cm]?ts|tsx)$/.test(entry))
|
|
1699
|
+
return [];
|
|
1700
|
+
const out = [];
|
|
1701
|
+
for (let i = 0; i < execArgv.length; i += 1) {
|
|
1702
|
+
const arg = execArgv[i];
|
|
1703
|
+
const eq = arg.indexOf("=");
|
|
1704
|
+
const flag = eq === -1 ? arg : arg.slice(0, eq);
|
|
1705
|
+
if (LOADER_FLAGS_BARE.has(arg)) {
|
|
1706
|
+
out.push(arg);
|
|
1707
|
+
continue;
|
|
1708
|
+
}
|
|
1709
|
+
if (!LOADER_FLAGS_WITH_VALUE.has(flag))
|
|
1710
|
+
continue;
|
|
1711
|
+
if (eq !== -1) {
|
|
1712
|
+
out.push(arg);
|
|
1713
|
+
}
|
|
1714
|
+
else if (i + 1 < execArgv.length) {
|
|
1715
|
+
out.push(arg, execArgv[i + 1]);
|
|
1716
|
+
i += 1;
|
|
1717
|
+
}
|
|
1718
|
+
}
|
|
1719
|
+
return out;
|
|
1720
|
+
}
|
|
1690
1721
|
/**
|
|
1691
1722
|
* How to re-invoke the running hq CLI without a PATH lookup. When argv[1] is
|
|
1692
1723
|
* the entry of an installed hq-cli package, run it with the same node binary;
|
|
@@ -1694,7 +1725,7 @@ const HQ_CLI_PACKAGE_NAME = "@indigoai-us/hq-cli";
|
|
|
1694
1725
|
* Falls back to `hq` on PATH when this process is not the hq CLI (tests,
|
|
1695
1726
|
* embedders). Exported for tests.
|
|
1696
1727
|
*/
|
|
1697
|
-
export function resolveHqInvocation(argv = process.argv, execPath = process.execPath) {
|
|
1728
|
+
export function resolveHqInvocation(argv = process.argv, execPath = process.execPath, execArgv = process.execArgv) {
|
|
1698
1729
|
const entry = argv[1];
|
|
1699
1730
|
if (entry) {
|
|
1700
1731
|
try {
|
|
@@ -1704,8 +1735,9 @@ export function resolveHqInvocation(argv = process.argv, execPath = process.exec
|
|
|
1704
1735
|
const manifest = path.join(dir, "package.json");
|
|
1705
1736
|
if (fs.existsSync(manifest)) {
|
|
1706
1737
|
const parsed = JSON.parse(fs.readFileSync(manifest, "utf8"));
|
|
1707
|
-
if (parsed.name === HQ_CLI_PACKAGE_NAME)
|
|
1708
|
-
return { command: execPath, args: [real] };
|
|
1738
|
+
if (parsed.name === HQ_CLI_PACKAGE_NAME) {
|
|
1739
|
+
return { command: execPath, args: [...loaderArgsForSource(real, execArgv), real] };
|
|
1740
|
+
}
|
|
1709
1741
|
break;
|
|
1710
1742
|
}
|
|
1711
1743
|
const parent = path.dirname(dir);
|
|
@@ -1720,12 +1752,20 @@ export function resolveHqInvocation(argv = process.argv, execPath = process.exec
|
|
|
1720
1752
|
}
|
|
1721
1753
|
return { command: "hq", args: [] };
|
|
1722
1754
|
}
|
|
1755
|
+
/** Output cap for the `hq ... --json` reads behind the refresh gates. */
|
|
1756
|
+
const HQ_JSON_MAX_BUFFER_BYTES = 64 * 1024 * 1024;
|
|
1757
|
+
const HQ_JSON_TIMEOUT_MS = 15_000;
|
|
1723
1758
|
/** Exported for tests; `invocation` defaults to {@link resolveHqInvocation}. */
|
|
1724
1759
|
export function execHqJson(args, invocation = resolveHqInvocation(), env = process.env) {
|
|
1725
1760
|
const result = spawnSync(invocation.command, [...invocation.args, ...args], {
|
|
1726
1761
|
env,
|
|
1727
1762
|
encoding: "utf8",
|
|
1728
|
-
|
|
1763
|
+
// The lane list takes ~2.5 s on a busy machine and grows with lane history; 5 s left too
|
|
1764
|
+
// little headroom under load, and a timeout fails the gate closed.
|
|
1765
|
+
timeout: HQ_JSON_TIMEOUT_MS,
|
|
1766
|
+
// `hq lanes list --json` grows with lane history (1.7 MB on a busy machine). The spawnSync
|
|
1767
|
+
// default of 1 MiB raised ENOBUFS, which made the live-lane gate fail closed on every collect.
|
|
1768
|
+
maxBuffer: HQ_JSON_MAX_BUFFER_BYTES,
|
|
1729
1769
|
windowsHide: true,
|
|
1730
1770
|
stdio: ["ignore", "pipe", "pipe"],
|
|
1731
1771
|
});
|
|
@@ -1733,6 +1773,8 @@ export function execHqJson(args, invocation = resolveHqInvocation(), env = proce
|
|
|
1733
1773
|
const code = result.error.code;
|
|
1734
1774
|
if (code === "ETIMEDOUT")
|
|
1735
1775
|
throw new Error(`hq ${args.join(" ")} timed out`);
|
|
1776
|
+
if (code === "ENOBUFS")
|
|
1777
|
+
throw new Error(`hq ${args.join(" ")} output exceeded ${HQ_JSON_MAX_BUFFER_BYTES} bytes`);
|
|
1736
1778
|
throw new Error(`hq ${args.join(" ")} failed to spawn: ${result.error.message}`);
|
|
1737
1779
|
}
|
|
1738
1780
|
if (result.signal)
|
|
@@ -1933,6 +1975,8 @@ const defaultListProcesses = async () => {
|
|
|
1933
1975
|
const result = spawnSync("ps", ["-axo", "pid=,command="], {
|
|
1934
1976
|
encoding: "utf8",
|
|
1935
1977
|
timeout: 5_000,
|
|
1978
|
+
// Full command lines for every process can pass the 1 MiB default on a busy machine.
|
|
1979
|
+
maxBuffer: HQ_JSON_MAX_BUFFER_BYTES,
|
|
1936
1980
|
windowsHide: true,
|
|
1937
1981
|
stdio: ["ignore", "pipe", "pipe"],
|
|
1938
1982
|
});
|
|
@@ -386,6 +386,13 @@ export declare function isExpiredReason(reason: string | null | undefined): bool
|
|
|
386
386
|
export declare function isAccountFailureReason(reason: string): boolean;
|
|
387
387
|
/** Exact command for an account that has no usable credential. */
|
|
388
388
|
export declare function capacityLoginCommand(provider: string, label: string): string;
|
|
389
|
+
/**
|
|
390
|
+
* Fix for a signed-out default home that no HQ account is registered on. `hq capacity login
|
|
391
|
+
* <provider> --account default` is refused (reserved id), and without --account it signs a new
|
|
392
|
+
* account into a new home, leaving the default home signed out. The vendor's own login in the
|
|
393
|
+
* default home is the one command that signs that home back in.
|
|
394
|
+
*/
|
|
395
|
+
export declare function defaultHomeLoginCommand(provider: string): string;
|
|
389
396
|
/** Open the vendor CLI so it can refresh its own token. Does not refresh anything. */
|
|
390
397
|
export declare function claudeOpenCommand(home: string | null, userHome?: string): string;
|
|
391
398
|
export declare function redactProviderTokens(text: string): string;
|
|
@@ -211,6 +211,20 @@ export function capacityLoginCommand(provider, label) {
|
|
|
211
211
|
const safe = /^[A-Za-z0-9._-]+$/.test(label) ? label : `'${label.replace(/'/g, `'\\''`)}'`;
|
|
212
212
|
return `hq capacity login ${provider} --account ${safe}`;
|
|
213
213
|
}
|
|
214
|
+
const DEFAULT_HOME_VENDOR_LOGIN = {
|
|
215
|
+
claude: "claude auth login",
|
|
216
|
+
codex: "codex login",
|
|
217
|
+
grok: "grok login",
|
|
218
|
+
};
|
|
219
|
+
/**
|
|
220
|
+
* Fix for a signed-out default home that no HQ account is registered on. `hq capacity login
|
|
221
|
+
* <provider> --account default` is refused (reserved id), and without --account it signs a new
|
|
222
|
+
* account into a new home, leaving the default home signed out. The vendor's own login in the
|
|
223
|
+
* default home is the one command that signs that home back in.
|
|
224
|
+
*/
|
|
225
|
+
export function defaultHomeLoginCommand(provider) {
|
|
226
|
+
return DEFAULT_HOME_VENDOR_LOGIN[provider] ?? capacityLoginCommand(provider, "default");
|
|
227
|
+
}
|
|
214
228
|
/** Open the vendor CLI so it can refresh its own token. Does not refresh anything. */
|
|
215
229
|
export function claudeOpenCommand(home, userHome = os.homedir()) {
|
|
216
230
|
const fallback = path.resolve(userHome, ".claude");
|
|
@@ -1053,7 +1067,9 @@ function clusterAccounts(provider, events) {
|
|
|
1053
1067
|
fixCommand: expired
|
|
1054
1068
|
? (provider.provider === "claude" ? claudeOpenCommand(home) : null)
|
|
1055
1069
|
: isSignedOut
|
|
1056
|
-
?
|
|
1070
|
+
? (primary === "default"
|
|
1071
|
+
? defaultHomeLoginCommand(provider.provider)
|
|
1072
|
+
: capacityLoginCommand(provider.provider, primary))
|
|
1057
1073
|
: null,
|
|
1058
1074
|
usageStale: usageStale ? true : undefined,
|
|
1059
1075
|
limits,
|
|
@@ -20,6 +20,8 @@ export interface RosterRow {
|
|
|
20
20
|
home: string;
|
|
21
21
|
/** True when the registry account is read for usage and is not used by lanes. */
|
|
22
22
|
usageOnly?: boolean;
|
|
23
|
+
/** The default home with no HQ account registered on it. Only the vendor login fixes it. */
|
|
24
|
+
unregisteredDefault?: boolean;
|
|
23
25
|
}
|
|
24
26
|
/** Sentence printed for an expired Claude credential. Other commands pass through. */
|
|
25
27
|
export declare function formatRefreshLine(provider: string, command: string): string;
|
|
@@ -9,7 +9,7 @@
|
|
|
9
9
|
import * as fs from "node:fs";
|
|
10
10
|
import * as path from "node:path";
|
|
11
11
|
import { defaultProviderHome } from "./accounts.js";
|
|
12
|
-
import { EXPIRED_REASON, claudeOpenCommand, isExpiredReason, } from "./ledger.js";
|
|
12
|
+
import { EXPIRED_REASON, capacityLoginCommand, claudeOpenCommand, defaultHomeLoginCommand, isExpiredReason, } from "./ledger.js";
|
|
13
13
|
import { effectiveAccountHome } from "../lanes/account-home.js";
|
|
14
14
|
import { canonicalizeHome, loadLanesConfig, writeLanesConfig } from "../lanes/config.js";
|
|
15
15
|
import { LANE_PROVIDERS, parseLaneProvider } from "../lanes/types.js";
|
|
@@ -132,11 +132,18 @@ export function rosterFrom(accounts, view, userHome) {
|
|
|
132
132
|
if (account.label === "default")
|
|
133
133
|
defaultHome.set(account.provider, path.resolve(account.home));
|
|
134
134
|
}
|
|
135
|
+
const registeredDefault = new Set();
|
|
136
|
+
for (const account of accounts) {
|
|
137
|
+
const home = defaultHome.get(account.provider);
|
|
138
|
+
if (account.label !== "default" && home && path.resolve(account.home) === home)
|
|
139
|
+
registeredDefault.add(account.provider);
|
|
140
|
+
}
|
|
135
141
|
const rows = [];
|
|
136
142
|
for (const account of accounts) {
|
|
137
143
|
const folded = defaultHome.get(account.provider);
|
|
138
144
|
if (account.label !== "default" && folded && path.resolve(account.home) === folded)
|
|
139
145
|
continue;
|
|
146
|
+
const unregisteredDefault = account.label === "default" && !registeredDefault.has(account.provider);
|
|
140
147
|
const group = view.providers.find((provider) => provider.provider === account.provider);
|
|
141
148
|
const match = matchAccount(group?.accounts, account);
|
|
142
149
|
const presented = match
|
|
@@ -149,11 +156,14 @@ export function rosterFrom(accounts, view, userHome) {
|
|
|
149
156
|
state: presented.state,
|
|
150
157
|
reason: presented.reason,
|
|
151
158
|
command: presented.state === "signed_out"
|
|
152
|
-
?
|
|
159
|
+
? unregisteredDefault
|
|
160
|
+
? defaultHomeLoginCommand(account.provider)
|
|
161
|
+
: presented.command ?? capacityLoginCommand(account.provider, account.label)
|
|
153
162
|
: presented.command,
|
|
154
163
|
observedAt: match?.observedAt ?? latestLimitAt(match),
|
|
155
164
|
home: account.home,
|
|
156
165
|
usageOnly: account.lanes === false,
|
|
166
|
+
...(unregisteredDefault ? { unregisteredDefault: true } : {}),
|
|
157
167
|
});
|
|
158
168
|
}
|
|
159
169
|
return rows;
|
|
@@ -225,7 +235,7 @@ function formatRepairPlan(plan) {
|
|
|
225
235
|
else {
|
|
226
236
|
lines.push("Signed out (login again):");
|
|
227
237
|
for (const row of plan.signedOut) {
|
|
228
|
-
lines.push(row.command ??
|
|
238
|
+
lines.push(row.command ?? capacityLoginCommand(row.provider, row.label));
|
|
229
239
|
}
|
|
230
240
|
}
|
|
231
241
|
if (plan.expired.length > 0) {
|
|
@@ -322,7 +332,12 @@ async function runRepair(request, deps) {
|
|
|
322
332
|
const fixed = [];
|
|
323
333
|
const skipped = [];
|
|
324
334
|
for (const row of plan.signedOut) {
|
|
325
|
-
const fix = row.command ??
|
|
335
|
+
const fix = row.command ?? capacityLoginCommand(row.provider, row.label);
|
|
336
|
+
if (row.unregisteredDefault) {
|
|
337
|
+
// No HQ account owns this home, so the lane login would refuse it. Name the vendor login.
|
|
338
|
+
skipped.push({ provider: row.provider, label: row.label, reason: "no HQ account uses the default home", fix });
|
|
339
|
+
continue;
|
|
340
|
+
}
|
|
326
341
|
let logged;
|
|
327
342
|
try {
|
|
328
343
|
logged = await deps.loginAccount(row.provider, row.label);
|
|
@@ -0,0 +1,78 @@
|
|
|
1
|
+
import * as fs from "node:fs";
|
|
2
|
+
import * as path from "node:path";
|
|
3
|
+
import { resolveSessionId } from "./session-id.js";
|
|
4
|
+
function metaValue(contents, key) {
|
|
5
|
+
for (const line of contents.split("\n")) {
|
|
6
|
+
const record = process.platform === "win32" ? line.replace(/\r$/, "") : line;
|
|
7
|
+
const first = record.match(/^\s*(\S+)/)?.[1] ?? "";
|
|
8
|
+
if (first !== `${key}:`)
|
|
9
|
+
continue;
|
|
10
|
+
return record.replace(/^[^:]+:[\t ]*/, "").replace(/^"|"$/g, "");
|
|
11
|
+
}
|
|
12
|
+
return "";
|
|
13
|
+
}
|
|
14
|
+
function listedCompanySlugs(contents) {
|
|
15
|
+
const listed = metaValue(contents, "company_slugs");
|
|
16
|
+
const primary = metaValue(contents, "company_slug");
|
|
17
|
+
const candidates = listed ? listed.split(",") : primary ? [primary] : [];
|
|
18
|
+
const seen = new Set();
|
|
19
|
+
const result = candidates.filter((slug) => {
|
|
20
|
+
if (!/^[A-Za-z0-9_-]+$/.test(slug) || seen.has(slug))
|
|
21
|
+
return false;
|
|
22
|
+
seen.add(slug);
|
|
23
|
+
return true;
|
|
24
|
+
});
|
|
25
|
+
if (result.length > 1 && result.includes("personal"))
|
|
26
|
+
return primary ? [primary] : [];
|
|
27
|
+
return primary && result[0] !== primary ? [primary] : result;
|
|
28
|
+
}
|
|
29
|
+
function isExactCompanyDirectory(root, slug) {
|
|
30
|
+
const companies = path.join(root, "companies");
|
|
31
|
+
try {
|
|
32
|
+
for (const entry of fs.readdirSync(companies)) {
|
|
33
|
+
const candidate = path.join(companies, entry);
|
|
34
|
+
try {
|
|
35
|
+
if (!fs.statSync(candidate).isDirectory() || fs.lstatSync(candidate).isSymbolicLink())
|
|
36
|
+
continue;
|
|
37
|
+
if (entry === slug)
|
|
38
|
+
return true;
|
|
39
|
+
}
|
|
40
|
+
catch (error) {
|
|
41
|
+
const code = error.code;
|
|
42
|
+
if (code !== "ENOENT" && code !== "ENOTDIR" && code !== "EACCES" && code !== "ELOOP")
|
|
43
|
+
throw error;
|
|
44
|
+
}
|
|
45
|
+
}
|
|
46
|
+
}
|
|
47
|
+
catch (error) {
|
|
48
|
+
const code = error.code;
|
|
49
|
+
if (code !== "ENOENT" && code !== "ENOTDIR" && code !== "EACCES" && code !== "ELOOP")
|
|
50
|
+
throw error;
|
|
51
|
+
}
|
|
52
|
+
return false;
|
|
53
|
+
}
|
|
54
|
+
/** Reads the active session's company lock without importing the full hq-session command. */
|
|
55
|
+
export function sessionCompanySlugs(root, env = process.env, multiCompanyLockEnabled = false) {
|
|
56
|
+
const sessionId = resolveSessionId(root, env);
|
|
57
|
+
if (!sessionId)
|
|
58
|
+
return [];
|
|
59
|
+
const metaPath = path.join(root, "workspace", "sessions", sessionId, "meta.yaml");
|
|
60
|
+
let contents;
|
|
61
|
+
try {
|
|
62
|
+
if (!fs.statSync(metaPath).isFile())
|
|
63
|
+
return [];
|
|
64
|
+
contents = fs.readFileSync(metaPath, "utf8");
|
|
65
|
+
}
|
|
66
|
+
catch (error) {
|
|
67
|
+
const code = error.code;
|
|
68
|
+
if (code === "ENOENT" || code === "ENOTDIR" || code === "EACCES" || code === "ELOOP")
|
|
69
|
+
return [];
|
|
70
|
+
throw error;
|
|
71
|
+
}
|
|
72
|
+
if (!multiCompanyLockEnabled) {
|
|
73
|
+
const primary = metaValue(contents, "company_slug");
|
|
74
|
+
return primary ? [primary] : [];
|
|
75
|
+
}
|
|
76
|
+
return listedCompanySlugs(contents).filter((slug) => slug === "personal" || isExactCompanyDirectory(root, slug));
|
|
77
|
+
}
|
|
78
|
+
//# sourceMappingURL=session-company.js.map
|
|
@@ -222,6 +222,7 @@ export interface ProviderResumeInput {
|
|
|
222
222
|
session_id: string;
|
|
223
223
|
prompt: string;
|
|
224
224
|
}
|
|
225
|
+
export declare function remoteControlLaneName(lane: Pick<LaneRecord, "lane_id" | "remote_control_title">): string;
|
|
225
226
|
export declare function buildProviderCommand(provider: LaneProvider, input: {
|
|
226
227
|
hqRoot: string;
|
|
227
228
|
briefPath: string;
|
package/dist/lib/lanes/spawn.js
CHANGED
|
@@ -1528,6 +1528,9 @@ export function envelopeRepairPrompt(input) {
|
|
|
1528
1528
|
`Output only that JSON. Do not add keys the schema does not name, and do not resume the task.`,
|
|
1529
1529
|
].join("\n\n");
|
|
1530
1530
|
}
|
|
1531
|
+
export function remoteControlLaneName(lane) {
|
|
1532
|
+
return lane.remote_control_title ?? `hq/${lane.lane_id}`;
|
|
1533
|
+
}
|
|
1531
1534
|
export function buildProviderCommand(provider, input) {
|
|
1532
1535
|
const parsed = parseLaneProvider(provider);
|
|
1533
1536
|
const command = PROVIDER_COMMAND[parsed];
|
|
@@ -2481,7 +2484,7 @@ export function spawnLaneWorker(hqRoot, lane, deps = {}, launch = {}) {
|
|
|
2481
2484
|
profile: profileLaunch,
|
|
2482
2485
|
workerPolicyDir,
|
|
2483
2486
|
resume,
|
|
2484
|
-
...(remoteSessionId ? { remoteControl: { sessionId: remoteSessionId, name:
|
|
2487
|
+
...(remoteSessionId ? { remoteControl: { sessionId: remoteSessionId, name: remoteControlLaneName(lane) } } : {}),
|
|
2485
2488
|
});
|
|
2486
2489
|
const remoteLaunch = remoteSessionId
|
|
2487
2490
|
? prepareRemoteControlLane({
|
package/dist/lib/lanes/store.js
CHANGED
|
@@ -433,6 +433,17 @@ export function parseLaneRecord(raw, filePath) {
|
|
|
433
433
|
value: raw.remote_control,
|
|
434
434
|
});
|
|
435
435
|
}
|
|
436
|
+
if (raw.remote_control_title !== undefined && (raw.remote_control !== true ||
|
|
437
|
+
typeof raw.remote_control_title !== "string" ||
|
|
438
|
+
raw.remote_control_title.trim() !== raw.remote_control_title ||
|
|
439
|
+
raw.remote_control_title.length < 1 ||
|
|
440
|
+
raw.remote_control_title.length > 80 ||
|
|
441
|
+
/\p{Cc}/u.test(raw.remote_control_title))) {
|
|
442
|
+
throw new LaneSchemaError("remote_control_title requires a Claude Remote Control lane and 1–80 printable characters", {
|
|
443
|
+
path: filePath,
|
|
444
|
+
field: "remote_control_title",
|
|
445
|
+
});
|
|
446
|
+
}
|
|
436
447
|
if (raw.written_by_version !== undefined) {
|
|
437
448
|
const writtenByVersion = requireString(raw.written_by_version, "written_by_version");
|
|
438
449
|
if (!semver.valid(writtenByVersion)) {
|
|
@@ -614,6 +614,8 @@ export interface LaneRecord {
|
|
|
614
614
|
provider: LaneProvider;
|
|
615
615
|
/** Open each Claude turn as a named Remote Control session using account a's login. */
|
|
616
616
|
remote_control?: boolean;
|
|
617
|
+
/** Human-readable Remote Control name, fixed when the lane is created. Older lanes may lack it. */
|
|
618
|
+
remote_control_title?: string;
|
|
617
619
|
/** Whether provider came from an explicit create override, the router (`--account auto`), or the worker profile/default. */
|
|
618
620
|
provider_source?: "cli" | "profile" | "default" | "auto";
|
|
619
621
|
account?: string;
|
package/dist/lib/lanes/types.js
CHANGED
|
@@ -235,7 +235,7 @@ export const LANE_FIELD_WRITERS = {
|
|
|
235
235
|
pickup_validation_pending_since: ["creator", "command", "spawn", "reconcile"],
|
|
236
236
|
priority: ["command"], worker: ["creator", "command"], worker_path: ["creator", "command"], worker_pack: ["creator", "command"], worker_profile: ["creator", "command"], worker_profile_hash: ["creator", "command", "spawn"], registry_worker: ["creator", "command", "spawn"], registry_worker_approval_history: ["command", "watcher", "reconcile"], worker_profile_repin_history: ["command"], worker_profile_ignored: ["creator", "command", "spawn"], worker_policy_delivery: ["command", "spawn"],
|
|
237
237
|
keep_alive: ["creator", "command"], keep_alive_standing: ["command"], keep_alive_opted_in_at: ["creator", "command"], keep_alive_blocked_reason: ["command", "reconcile"], keep_alive_resume_count: ["command", "reconcile", "spawn"], keep_alive_history: ["reconcile"], keep_alive_blocked_resume_streak: ["reconcile", "spawn", "watcher"], keep_alive_last_resume_at: ["command", "reconcile", "spawn"], keep_alive_next_eligible_at: ["command", "reconcile", "spawn"], keep_alive_exhausted_at: ["command", "reconcile", "spawn"], loop_mode: ["creator"],
|
|
238
|
-
provider: ["creator", "command"], remote_control: ["creator"], provider_source: ["creator", "command"], account: ["creator", "spawn", "command", "watcher", "reconcile"], account_source: ["creator", "command"], profile_settings_pinned: ["command"], account_selection: ["creator", "command"], account_pick: ["creator"], admission_accounts: ["creator", "command"], account_history: ["command", "spawn", "watcher", "reconcile"], failed_resume_history: ["command"], fallback_question_cancellation_failures: ["command"], pending_resume: ["command", "spawn", "watcher", "reconcile"], placement_default: ["creator"], host: ["creator", "command"], origin_host: ["creator"], origin_host_source: ["creator"],
|
|
238
|
+
provider: ["creator", "command"], remote_control: ["creator"], remote_control_title: ["creator"], provider_source: ["creator", "command"], account: ["creator", "spawn", "command", "watcher", "reconcile"], account_source: ["creator", "command"], profile_settings_pinned: ["command"], account_selection: ["creator", "command"], account_pick: ["creator"], admission_accounts: ["creator", "command"], account_history: ["command", "spawn", "watcher", "reconcile"], failed_resume_history: ["command"], fallback_question_cancellation_failures: ["command"], pending_resume: ["command", "spawn", "watcher", "reconcile"], placement_default: ["creator"], host: ["creator", "command"], origin_host: ["creator"], origin_host_source: ["creator"],
|
|
239
239
|
version: ["creator"], created_at: ["creator"], updated_at: ["creator"], ended_at: ["reconcile"], envelope_summary: ["reconcile"], attribution_history: ["command", "watcher", "reconcile"], story_switch_pending: ["command", "watcher", "reconcile"], story_switch_unacknowledged: ["command", "watcher", "reconcile"],
|
|
240
240
|
last_envelope: ["watcher", "reconcile"], model: ["creator", "spawn", "command"], model_source: ["creator", "spawn", "command"], effort: ["creator", "spawn", "command"], effort_source: ["creator", "spawn", "command"], fast_mode: ["spawn"],
|
|
241
241
|
last_message_at: ["creator", "command"], status_gate: ["spawn", "watcher"], status_gate_history: ["watcher", "reconcile"], blocker_freshness_state: ["watcher"], kr_freshness_state: ["watcher"], last_compaction: ["spawn"], story_complete: ["watcher", "reconcile"], story_complete_reason: ["watcher", "reconcile"], story_status: ["watcher", "reconcile"],
|