@indigoai-us/hq-cli 5.307.0 → 5.308.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.
- package/CHANGELOG.md +2 -1
- package/assets/scaffold/core/scripts/resolve-company.sh +199 -0
- package/dist/command-catalog.generated.d.ts +13 -0
- package/dist/command-catalog.generated.js +16 -0
- package/dist/commands/core-resolve-company-command.d.ts +4 -0
- package/dist/commands/core-resolve-company-command.js +42 -0
- package/dist/commands/core-resolve-company-native.d.ts +28 -0
- package/dist/commands/core-resolve-company-native.js +408 -0
- package/dist/commands/core.js +3 -0
- package/dist/fast-scaffold.js +12 -0
- package/package.json +1 -1
package/CHANGELOG.md
CHANGED
|
@@ -2,7 +2,8 @@
|
|
|
2
2
|
|
|
3
3
|
## [Unreleased]
|
|
4
4
|
|
|
5
|
-
## [5.
|
|
5
|
+
## [5.308.0] — 2026-10-01
|
|
6
|
+
- Add native `hq core resolve-company` on the fast route behind the default-off `core-native-utilities` gate.
|
|
6
7
|
|
|
7
8
|
- Stop immediately when hq-deploy repeats an app-list cursor while resolving apps.
|
|
8
9
|
|
|
@@ -0,0 +1,199 @@
|
|
|
1
|
+
#!/usr/bin/env bash
|
|
2
|
+
# hq-core: public
|
|
3
|
+
# resolve-company.sh — resolve the active company for skills and routing.
|
|
4
|
+
#
|
|
5
|
+
# Resolution order (first hit wins): explicit manifest slug in the prompt,
|
|
6
|
+
# session company_slug, enabled device default, then none. Prompt matching is
|
|
7
|
+
# whole-token only; free text never guesses a company.
|
|
8
|
+
#
|
|
9
|
+
# Output: {"company":"<slug>","source":"prompt|session|device_default|none"}
|
|
10
|
+
# Always exits 0 and never writes state.
|
|
11
|
+
|
|
12
|
+
set -uo pipefail
|
|
13
|
+
|
|
14
|
+
ROOT=""
|
|
15
|
+
PROMPT=""
|
|
16
|
+
HAVE_PROMPT=0
|
|
17
|
+
|
|
18
|
+
while [ $# -gt 0 ]; do
|
|
19
|
+
case "$1" in
|
|
20
|
+
--root) ROOT="${2:-}"; shift 2 || shift ;;
|
|
21
|
+
--prompt) PROMPT="${2:-}"; HAVE_PROMPT=1; shift 2 || shift ;;
|
|
22
|
+
--help|-h)
|
|
23
|
+
sed -n '2,14p' "${BASH_SOURCE[0]}" | sed 's/^# \{0,1\}//'
|
|
24
|
+
exit 0
|
|
25
|
+
;;
|
|
26
|
+
*) shift ;;
|
|
27
|
+
esac
|
|
28
|
+
done
|
|
29
|
+
|
|
30
|
+
if [ "$HAVE_PROMPT" -eq 0 ] && [ ! -t 0 ]; then
|
|
31
|
+
PROMPT="$(cat 2>/dev/null || true)"
|
|
32
|
+
fi
|
|
33
|
+
: "${PROMPT:=}"
|
|
34
|
+
|
|
35
|
+
emit() {
|
|
36
|
+
printf '{"company":"%s","source":"%s"}\n' "$1" "$2"
|
|
37
|
+
exit 0
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
if [ -z "$ROOT" ]; then
|
|
41
|
+
ROOT="${HQ_ROOT:-${CLAUDE_PROJECT_DIR:-$(cd "$(dirname "${BASH_SOURCE[0]}")/.." && cd .. && pwd)}}"
|
|
42
|
+
fi
|
|
43
|
+
|
|
44
|
+
MANIFEST="$ROOT/companies/manifest.yaml"
|
|
45
|
+
[ -f "$MANIFEST" ] || emit "" "none"
|
|
46
|
+
|
|
47
|
+
SLUGS="$(
|
|
48
|
+
awk '
|
|
49
|
+
function keep(slug) {
|
|
50
|
+
return slug != "" && slug != "_template" && slug != "companies" && slug != "unaffiliated_repos"
|
|
51
|
+
}
|
|
52
|
+
/^companies:[[:space:]]*$/ { wrapped = 1; next }
|
|
53
|
+
wrapped && /^[^[:space:]][^:]*:[[:space:]]*$/ { wrapped = 0 }
|
|
54
|
+
wrapped && /^ [A-Za-z][A-Za-z0-9_-]*:/ {
|
|
55
|
+
line = $0; sub(/^[[:space:]]+/, "", line); sub(/:.*/, "", line)
|
|
56
|
+
if (keep(line)) print line
|
|
57
|
+
next
|
|
58
|
+
}
|
|
59
|
+
!wrapped && /^[A-Za-z][A-Za-z0-9_-]*:/ {
|
|
60
|
+
line = $0; sub(/:.*/, "", line)
|
|
61
|
+
if (keep(line)) print line
|
|
62
|
+
}
|
|
63
|
+
' "$MANIFEST" | sort -u
|
|
64
|
+
)"
|
|
65
|
+
[ -n "$SLUGS" ] || emit "" "none"
|
|
66
|
+
|
|
67
|
+
is_known_slug() {
|
|
68
|
+
printf '%s\n' "$SLUGS" | grep -Fxq "$1"
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
prompt_slug() {
|
|
72
|
+
local normalized="" match=""
|
|
73
|
+
[ -n "$PROMPT" ] || return 0
|
|
74
|
+
normalized="$(printf '%s' "$PROMPT" | tr '[:upper:]' '[:lower:]' | tr -c 'a-z0-9_-' ' ')"
|
|
75
|
+
match="$(
|
|
76
|
+
# Prompt normalization is case-insensitive, but company IDs are not.
|
|
77
|
+
# Keep a folded match only when exactly one registered ID has that key.
|
|
78
|
+
# If IDs differ only by case, do not guess which tenant the prompt means.
|
|
79
|
+
printf '%s\n' "$SLUGS" | awk -v line="$normalized" '
|
|
80
|
+
BEGIN {
|
|
81
|
+
n = split(line, tok, /[ \t]+/)
|
|
82
|
+
for (i = 1; i <= n; i++) if (tok[i] != "" && !(tok[i] in pos)) pos[tok[i]] = i
|
|
83
|
+
bestSlug = ""; bestLen = 0; bestPos = 0
|
|
84
|
+
}
|
|
85
|
+
{
|
|
86
|
+
slug = $0
|
|
87
|
+
key = tolower(slug)
|
|
88
|
+
if (key in pos) {
|
|
89
|
+
matches[key]++
|
|
90
|
+
candidate[key] = slug
|
|
91
|
+
}
|
|
92
|
+
}
|
|
93
|
+
END {
|
|
94
|
+
for (key in matches) {
|
|
95
|
+
if (matches[key] != 1) continue
|
|
96
|
+
slug = candidate[key]
|
|
97
|
+
len = length(slug); p = pos[key]
|
|
98
|
+
if (len > bestLen || (len == bestLen && p < bestPos)) {
|
|
99
|
+
bestSlug = slug; bestLen = len; bestPos = p
|
|
100
|
+
}
|
|
101
|
+
}
|
|
102
|
+
if (bestSlug != "") print bestSlug
|
|
103
|
+
}
|
|
104
|
+
'
|
|
105
|
+
)"
|
|
106
|
+
printf '%s' "$match"
|
|
107
|
+
}
|
|
108
|
+
|
|
109
|
+
MATCH="$(prompt_slug)"
|
|
110
|
+
[ -n "$MATCH" ] && emit "$MATCH" "prompt"
|
|
111
|
+
|
|
112
|
+
SESSION_CO=""
|
|
113
|
+
if [ -x "$ROOT/core/scripts/hq-session.sh" ]; then
|
|
114
|
+
SESSION_CO="$(bash "$ROOT/core/scripts/hq-session.sh" get company_slug 2>/dev/null || true)"
|
|
115
|
+
SESSION_CO="$(printf '%s' "$SESSION_CO" | tr -d '[:space:]\"')"
|
|
116
|
+
fi
|
|
117
|
+
if [ -n "$SESSION_CO" ] && is_known_slug "$SESSION_CO"; then
|
|
118
|
+
emit "$SESSION_CO" "session"
|
|
119
|
+
fi
|
|
120
|
+
|
|
121
|
+
is_fleet_identity() {
|
|
122
|
+
[ -n "${HQ_AGENT_WORKDIR:-}" ] && return 0
|
|
123
|
+
[ -n "${HQ_AGENT_COMPANY_DIR:-}" ] && return 0
|
|
124
|
+
[ -n "${HQ_AGENT_IDENTITY_FILE:-}" ] && return 0
|
|
125
|
+
[ -f /etc/hq-agent/identity.json ] && return 0
|
|
126
|
+
[ -f /etc/hq-agent/machine-creds.json ] && return 0
|
|
127
|
+
agent_root="${HQ_AGENT_ROOT_PREFIX:-}"
|
|
128
|
+
if [ -n "$agent_root" ]; then
|
|
129
|
+
[ -f "${agent_root%/}/var/lib/hq-agent/identity.json" ] && return 0
|
|
130
|
+
else
|
|
131
|
+
[ -f /var/lib/hq-agent/identity.json ] && return 0
|
|
132
|
+
fi
|
|
133
|
+
[ -f /var/lib/hq-agent/machine-creds.json ] && return 0
|
|
134
|
+
return 1
|
|
135
|
+
}
|
|
136
|
+
|
|
137
|
+
run_with_timeout() {
|
|
138
|
+
command -v node >/dev/null 2>&1 || return 127
|
|
139
|
+
node -e '
|
|
140
|
+
const { spawn } = require("child_process");
|
|
141
|
+
const command = process.argv[1];
|
|
142
|
+
if (!command) process.exit(127);
|
|
143
|
+
const child = spawn(command, process.argv.slice(2), { detached: true, stdio: ["ignore", "pipe", "ignore"] });
|
|
144
|
+
let finished = false;
|
|
145
|
+
const finish = (status) => {
|
|
146
|
+
if (finished) return;
|
|
147
|
+
finished = true;
|
|
148
|
+
clearTimeout(timer);
|
|
149
|
+
try { process.kill(-child.pid, "SIGTERM"); } catch (_) {}
|
|
150
|
+
process.exit(status);
|
|
151
|
+
};
|
|
152
|
+
child.stdout.on("data", (chunk) => process.stdout.write(chunk));
|
|
153
|
+
child.once("error", () => finish(127));
|
|
154
|
+
child.once("exit", (code) => finish(typeof code === "number" ? code : 1));
|
|
155
|
+
const timer = setTimeout(() => finish(124), 2000);
|
|
156
|
+
' "$@"
|
|
157
|
+
}
|
|
158
|
+
|
|
159
|
+
if ! is_fleet_identity && command -v hq >/dev/null 2>&1 && command -v jq >/dev/null 2>&1; then
|
|
160
|
+
DEFAULT_JSON="$(run_with_timeout hq mesh context default get --json 2>/dev/null || true)"
|
|
161
|
+
DEFAULT_SLUG="$(printf '%s' "$DEFAULT_JSON" | jq -er '(.defaultCompany // .) as $default | if $default.enabled == true and ($default.source // "") != "disabled" and ($default.needsChoice // false) == false and ($default.slug | type == "string") then $default.slug else empty end' 2>/dev/null || true)"
|
|
162
|
+
DEFAULT_SLUG="$(printf '%s' "$DEFAULT_SLUG" | tr -d '[:space:]\"')"
|
|
163
|
+
if [ -n "$DEFAULT_SLUG" ] && is_known_slug "$DEFAULT_SLUG"; then
|
|
164
|
+
# A device default is only a local preference. Reconcile performs the
|
|
165
|
+
# token-backed active-membership check; never promote this fallback when
|
|
166
|
+
# that preflight is unavailable, times out, or returns a noncanonical row.
|
|
167
|
+
PREFLIGHT_SESSION_ID="resolve-default-$$-$RANDOM"
|
|
168
|
+
PREFLIGHT_OPERATION_ID="resolve-default-op-$$-$RANDOM"
|
|
169
|
+
PREFLIGHT_OBSERVATION="$(jq -cn \
|
|
170
|
+
--arg session_id "$PREFLIGHT_SESSION_ID" \
|
|
171
|
+
--arg operation_id "$PREFLIGHT_OPERATION_ID" \
|
|
172
|
+
--arg root "$ROOT" \
|
|
173
|
+
'{contractVersion:1,clientOperationId:$operation_id,identity:{sessionId:$session_id,harness:"hq-core",adapterVersion:"1"},cwd:$root,hqRoot:$root}')"
|
|
174
|
+
PREFLIGHT_STATUS=0
|
|
175
|
+
PREFLIGHT_RESULT="$(run_with_timeout hq mesh context reconcile --observation-json "$PREFLIGHT_OBSERVATION" --machine --offline 2>/dev/null)" || PREFLIGHT_STATUS=$?
|
|
176
|
+
VALIDATED_SLUG=""
|
|
177
|
+
if [ "$PREFLIGHT_STATUS" -eq 0 ]; then
|
|
178
|
+
VALIDATED_SLUG="$(printf '%s' "$PREFLIGHT_RESULT" | jq -er \
|
|
179
|
+
--arg slug "$DEFAULT_SLUG" \
|
|
180
|
+
--arg session_id "$PREFLIGHT_SESSION_ID" \
|
|
181
|
+
--arg operation_id "$PREFLIGHT_OPERATION_ID" '
|
|
182
|
+
if type == "object"
|
|
183
|
+
and .contractVersion == 1
|
|
184
|
+
and (.sessionId | type == "string") and .sessionId == $session_id
|
|
185
|
+
and (.clientOperationId | type == "string") and .clientOperationId == $operation_id
|
|
186
|
+
and (.kind == "queued" or .kind == "needs_project" or .kind == "needs_task" or .kind == "bound")
|
|
187
|
+
and (.classification == "needs_project" or .classification == "needs_task" or .classification == "bound")
|
|
188
|
+
and (.delivery == "queued" or .delivery == "acked")
|
|
189
|
+
and .lifecycle == "open"
|
|
190
|
+
and .companySlug == $slug
|
|
191
|
+
and (.companyUid | type == "string") and (.companyUid | length > 0)
|
|
192
|
+
then .companySlug else empty end
|
|
193
|
+
' 2>/dev/null || true)"
|
|
194
|
+
fi
|
|
195
|
+
[ "$VALIDATED_SLUG" = "$DEFAULT_SLUG" ] && emit "$DEFAULT_SLUG" "device_default"
|
|
196
|
+
fi
|
|
197
|
+
fi
|
|
198
|
+
|
|
199
|
+
emit "" "none"
|
|
@@ -5730,6 +5730,19 @@ export declare const COMMAND_CATALOG: readonly [{
|
|
|
5730
5730
|
readonly options: readonly [];
|
|
5731
5731
|
readonly subcommands: readonly [];
|
|
5732
5732
|
}];
|
|
5733
|
+
}, {
|
|
5734
|
+
readonly name: "resolve-company";
|
|
5735
|
+
readonly description: "Resolve the active company for skills and routing";
|
|
5736
|
+
readonly aliases: readonly [];
|
|
5737
|
+
readonly hidden: false;
|
|
5738
|
+
readonly usage: "[args...]";
|
|
5739
|
+
readonly arguments: readonly [{
|
|
5740
|
+
readonly name: "args";
|
|
5741
|
+
readonly required: false;
|
|
5742
|
+
readonly variadic: true;
|
|
5743
|
+
}];
|
|
5744
|
+
readonly options: readonly [];
|
|
5745
|
+
readonly subcommands: readonly [];
|
|
5733
5746
|
}, {
|
|
5734
5747
|
readonly name: "audit-log";
|
|
5735
5748
|
readonly description: "Append, query, or summarize the HQ audit log";
|
|
@@ -7384,6 +7384,22 @@ export const COMMAND_CATALOG = [
|
|
|
7384
7384
|
}
|
|
7385
7385
|
]
|
|
7386
7386
|
},
|
|
7387
|
+
{
|
|
7388
|
+
"name": "resolve-company",
|
|
7389
|
+
"description": "Resolve the active company for skills and routing",
|
|
7390
|
+
"aliases": [],
|
|
7391
|
+
"hidden": false,
|
|
7392
|
+
"usage": "[args...]",
|
|
7393
|
+
"arguments": [
|
|
7394
|
+
{
|
|
7395
|
+
"name": "args",
|
|
7396
|
+
"required": false,
|
|
7397
|
+
"variadic": true
|
|
7398
|
+
}
|
|
7399
|
+
],
|
|
7400
|
+
"options": [],
|
|
7401
|
+
"subcommands": []
|
|
7402
|
+
},
|
|
7387
7403
|
{
|
|
7388
7404
|
"name": "audit-log",
|
|
7389
7405
|
"description": "Append, query, or summarize the HQ audit log",
|
|
@@ -0,0 +1,4 @@
|
|
|
1
|
+
import type { Command } from "commander";
|
|
2
|
+
/** Commander registration stays in the full graph; the native resolver is fast-path safe. */
|
|
3
|
+
export declare function registerCoreResolveCompanyNativeCommand(core: Command): void;
|
|
4
|
+
//# sourceMappingURL=core-resolve-company-command.d.ts.map
|
|
@@ -0,0 +1,42 @@
|
|
|
1
|
+
import { resolveLiveRoot } from "../utils/hq-roots.js";
|
|
2
|
+
import { runBundledScript } from "../utils/run-bundled-script.js";
|
|
3
|
+
import { describeCoreCommand } from "./core-command-catalog.js";
|
|
4
|
+
import { coreNativeUtilitiesEnabled } from "./core-native-utilities-flag.js";
|
|
5
|
+
import { RESOLVE_COMPANY_ASSET, resolveCompany } from "./core-resolve-company-native.js";
|
|
6
|
+
/** Commander registration stays in the full graph; the native resolver is fast-path safe. */
|
|
7
|
+
export function registerCoreResolveCompanyNativeCommand(core) {
|
|
8
|
+
const command = core.command("resolve-company")
|
|
9
|
+
.description("Resolve the active company for skills and routing")
|
|
10
|
+
.allowUnknownOption()
|
|
11
|
+
.allowExcessArguments()
|
|
12
|
+
.helpOption(false)
|
|
13
|
+
.argument("[args...]")
|
|
14
|
+
.action(async (args = [], _options, cmd) => {
|
|
15
|
+
const operands = cmd.args.length > 0 ? cmd.args : args;
|
|
16
|
+
if (!await coreNativeUtilitiesEnabled()) {
|
|
17
|
+
const root = resolveLiveRoot({ hqRoot: core.opts().hqRoot });
|
|
18
|
+
const { code } = runBundledScript({
|
|
19
|
+
asset: RESOLVE_COMPANY_ASSET,
|
|
20
|
+
args: operands,
|
|
21
|
+
cwd: root,
|
|
22
|
+
hqRoot: root,
|
|
23
|
+
});
|
|
24
|
+
if (code !== 0)
|
|
25
|
+
process.exit(code);
|
|
26
|
+
return;
|
|
27
|
+
}
|
|
28
|
+
const root = resolveLiveRoot({ hqRoot: core.opts().hqRoot });
|
|
29
|
+
const result = await resolveCompany(operands, {
|
|
30
|
+
cwd: process.cwd(),
|
|
31
|
+
hqRoot: root,
|
|
32
|
+
stdinIsTty: process.stdin.isTTY === true,
|
|
33
|
+
});
|
|
34
|
+
process.stdout.write(result.stdout);
|
|
35
|
+
if (result.stderr.length)
|
|
36
|
+
process.stderr.write(result.stderr);
|
|
37
|
+
if (result.code !== 0)
|
|
38
|
+
process.exit(result.code);
|
|
39
|
+
});
|
|
40
|
+
describeCoreCommand(command, { execution: "native", asset: RESOLVE_COMPANY_ASSET, root: "live" });
|
|
41
|
+
}
|
|
42
|
+
//# sourceMappingURL=core-resolve-company-command.js.map
|
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
import * as fs from "node:fs";
|
|
2
|
+
export declare const RESOLVE_COMPANY_ASSET = "core/scripts/resolve-company.sh";
|
|
3
|
+
export declare const RESOLVE_COMPANY_HELP: string;
|
|
4
|
+
export type ResolveCompanyResult = {
|
|
5
|
+
code: number;
|
|
6
|
+
stdout: Buffer;
|
|
7
|
+
stderr: Buffer;
|
|
8
|
+
};
|
|
9
|
+
type HqResult = {
|
|
10
|
+
code: number;
|
|
11
|
+
stdout: string;
|
|
12
|
+
};
|
|
13
|
+
type ResolveCompanyFs = Pick<typeof fs, "readFileSync" | "statSync" | "accessSync">;
|
|
14
|
+
export type ResolveCompanyDependencies = {
|
|
15
|
+
cwd: string;
|
|
16
|
+
hqRoot?: string;
|
|
17
|
+
env?: NodeJS.ProcessEnv;
|
|
18
|
+
fs?: ResolveCompanyFs;
|
|
19
|
+
stdin?: string;
|
|
20
|
+
stdinIsTty?: boolean;
|
|
21
|
+
spawnHq?: (args: string[], env: NodeJS.ProcessEnv, timeoutMs: number, cwd?: string) => Promise<HqResult>;
|
|
22
|
+
pid?: number;
|
|
23
|
+
random?: () => number;
|
|
24
|
+
};
|
|
25
|
+
export declare function resolveCompany(args: readonly string[], dependencies: ResolveCompanyDependencies): Promise<ResolveCompanyResult>;
|
|
26
|
+
export declare function runResolveCompanyFast(args: readonly string[], stdin: string, cwd: string, hqRoot: string | undefined, _commandName?: string, _hqRootArg?: string, stdinReader?: () => Promise<string>): Promise<number>;
|
|
27
|
+
export {};
|
|
28
|
+
//# sourceMappingURL=core-resolve-company-native.d.ts.map
|
|
@@ -0,0 +1,408 @@
|
|
|
1
|
+
/** Native port of resolve-company.sh. This module stays on the fast import path. */
|
|
2
|
+
import { spawn } from "node:child_process";
|
|
3
|
+
import * as fs from "node:fs";
|
|
4
|
+
import * as path from "node:path";
|
|
5
|
+
export const RESOLVE_COMPANY_ASSET = "core/scripts/resolve-company.sh";
|
|
6
|
+
export const RESOLVE_COMPANY_HELP = [
|
|
7
|
+
"hq-core: public",
|
|
8
|
+
"resolve-company.sh — resolve the active company for skills and routing.",
|
|
9
|
+
"",
|
|
10
|
+
"Resolution order (first hit wins): explicit manifest slug in the prompt,",
|
|
11
|
+
"session company_slug, enabled device default, then none. Prompt matching is",
|
|
12
|
+
"whole-token only; free text never guesses a company.",
|
|
13
|
+
"",
|
|
14
|
+
"Output: {\"company\":\"<slug>\",\"source\":\"prompt|session|device_default|none\"}",
|
|
15
|
+
"Always exits 0 and never writes state.",
|
|
16
|
+
"",
|
|
17
|
+
"set -uo pipefail",
|
|
18
|
+
"",
|
|
19
|
+
"ROOT=\"\"",
|
|
20
|
+
].join("\n") + "\n";
|
|
21
|
+
const EMPTY_ENV = {};
|
|
22
|
+
const keepSlug = (slug) => slug !== "" && slug !== "_template" && slug !== "companies" && slug !== "unaffiliated_repos";
|
|
23
|
+
function errorCode(error) {
|
|
24
|
+
return error && typeof error === "object" && "code" in error && typeof error.code === "string"
|
|
25
|
+
? error.code
|
|
26
|
+
: undefined;
|
|
27
|
+
}
|
|
28
|
+
function readFile(filePath, fileSystem) {
|
|
29
|
+
try {
|
|
30
|
+
return fileSystem.readFileSync(filePath, "utf8");
|
|
31
|
+
}
|
|
32
|
+
catch (error) {
|
|
33
|
+
const code = errorCode(error);
|
|
34
|
+
if (code === "ENOENT" || code === "ENOTDIR" || code === "EACCES" || code === "ELOOP")
|
|
35
|
+
return undefined;
|
|
36
|
+
throw error;
|
|
37
|
+
}
|
|
38
|
+
}
|
|
39
|
+
function readManifest(filePath, fileSystem) {
|
|
40
|
+
try {
|
|
41
|
+
return fileSystem.readFileSync(filePath, "utf8");
|
|
42
|
+
}
|
|
43
|
+
catch (error) {
|
|
44
|
+
const code = errorCode(error);
|
|
45
|
+
if (code === "EACCES") {
|
|
46
|
+
return {
|
|
47
|
+
code: 0,
|
|
48
|
+
stdout: Buffer.from('{"company":"","source":"none"}\n'),
|
|
49
|
+
stderr: Buffer.from("awk: fatal: cannot open file `" + filePath + "' for reading: Permission denied\n"),
|
|
50
|
+
};
|
|
51
|
+
}
|
|
52
|
+
if (code === "ENOENT" || code === "ENOTDIR" || code === "ELOOP")
|
|
53
|
+
return output("", "none");
|
|
54
|
+
throw error;
|
|
55
|
+
}
|
|
56
|
+
}
|
|
57
|
+
function isFile(filePath, fileSystem) {
|
|
58
|
+
try {
|
|
59
|
+
return fileSystem.statSync(filePath).isFile();
|
|
60
|
+
}
|
|
61
|
+
catch (error) {
|
|
62
|
+
const code = errorCode(error);
|
|
63
|
+
if (code === "ENOENT" || code === "ENOTDIR" || code === "EACCES" || code === "ELOOP")
|
|
64
|
+
return false;
|
|
65
|
+
throw error;
|
|
66
|
+
}
|
|
67
|
+
}
|
|
68
|
+
function parseManifestSlugs(text) {
|
|
69
|
+
const slugs = new Set();
|
|
70
|
+
let wrapped = false;
|
|
71
|
+
for (const rawLine of text.split("\n")) {
|
|
72
|
+
const line = rawLine.endsWith("\r") ? rawLine.slice(0, -1) : rawLine;
|
|
73
|
+
if (/^companies:[ \t]*$/.test(line)) {
|
|
74
|
+
wrapped = true;
|
|
75
|
+
continue;
|
|
76
|
+
}
|
|
77
|
+
if (wrapped && /^[^ \t][^:]*:[ \t]*$/.test(line))
|
|
78
|
+
wrapped = false;
|
|
79
|
+
if (wrapped) {
|
|
80
|
+
const match = line.match(/^ {2}([A-Za-z][A-Za-z0-9_-]*):/);
|
|
81
|
+
if (match && keepSlug(match[1]))
|
|
82
|
+
slugs.add(match[1]);
|
|
83
|
+
continue;
|
|
84
|
+
}
|
|
85
|
+
const match = line.match(/^([A-Za-z][A-Za-z0-9_-]*):/);
|
|
86
|
+
if (match && keepSlug(match[1]))
|
|
87
|
+
slugs.add(match[1]);
|
|
88
|
+
}
|
|
89
|
+
return [...slugs];
|
|
90
|
+
}
|
|
91
|
+
function promptSlug(prompt, slugs) {
|
|
92
|
+
if (!prompt)
|
|
93
|
+
return "";
|
|
94
|
+
const asciiLower = (value) => value.replace(/[A-Z]/g, (letter) => String.fromCharCode(letter.charCodeAt(0) + 32));
|
|
95
|
+
const normalized = asciiLower(prompt).replace(/[^a-z0-9_-]/g, " ");
|
|
96
|
+
const positions = new Map();
|
|
97
|
+
normalized.split(/[ \t]+/).forEach((token, index) => {
|
|
98
|
+
if (token && !positions.has(token))
|
|
99
|
+
positions.set(token, index + 1);
|
|
100
|
+
});
|
|
101
|
+
const matches = new Map();
|
|
102
|
+
for (const slug of slugs) {
|
|
103
|
+
const key = asciiLower(slug);
|
|
104
|
+
if (!positions.has(key))
|
|
105
|
+
continue;
|
|
106
|
+
matches.set(key, [...(matches.get(key) ?? []), slug]);
|
|
107
|
+
}
|
|
108
|
+
let bestSlug = "";
|
|
109
|
+
let bestLength = 0;
|
|
110
|
+
let bestPosition = 0;
|
|
111
|
+
for (const [key, candidates] of matches) {
|
|
112
|
+
if (candidates.length !== 1)
|
|
113
|
+
continue;
|
|
114
|
+
const slug = candidates[0];
|
|
115
|
+
const length = slug.length;
|
|
116
|
+
const position = positions.get(key);
|
|
117
|
+
if (length > bestLength || (length === bestLength && position < bestPosition)) {
|
|
118
|
+
bestSlug = slug;
|
|
119
|
+
bestLength = length;
|
|
120
|
+
bestPosition = position;
|
|
121
|
+
}
|
|
122
|
+
}
|
|
123
|
+
return bestSlug;
|
|
124
|
+
}
|
|
125
|
+
function validSessionId(value) {
|
|
126
|
+
return value !== "" && value !== "." && value !== ".." && /^[A-Za-z0-9._-]+$/.test(value);
|
|
127
|
+
}
|
|
128
|
+
function sessionId(root, env, fileSystem) {
|
|
129
|
+
for (const name of ["HQ_SESSION_ID", "CLAUDE_CODE_SESSION_ID", "CLAUDE_SESSION_ID", "CODEX_SESSION_ID", "CODEX_THREAD_ID", "GROK_SESSION_ID"]) {
|
|
130
|
+
const value = (env[name] ?? "").replace(/[ \t\n\v\f\r]/g, "");
|
|
131
|
+
if (validSessionId(value))
|
|
132
|
+
return value;
|
|
133
|
+
}
|
|
134
|
+
const current = readFile(path.join(root, "workspace/sessions/.current"), fileSystem)?.replace(/[ \t\n\v\f\r]/g, "") ?? "";
|
|
135
|
+
return validSessionId(current) ? current : "";
|
|
136
|
+
}
|
|
137
|
+
function sessionCompany(root, env, fileSystem) {
|
|
138
|
+
const helper = path.join(root, "core/scripts/hq-session.sh");
|
|
139
|
+
try {
|
|
140
|
+
fileSystem.accessSync(helper, fs.constants.X_OK);
|
|
141
|
+
}
|
|
142
|
+
catch (error) {
|
|
143
|
+
const code = errorCode(error);
|
|
144
|
+
if (code === "ENOENT" || code === "ENOTDIR" || code === "EACCES" || code === "ELOOP")
|
|
145
|
+
return "";
|
|
146
|
+
throw error;
|
|
147
|
+
}
|
|
148
|
+
// A directory may be executable according to access(2), but `bash directory`
|
|
149
|
+
// fails without output, just as hq-session.sh does here.
|
|
150
|
+
if (!isFile(helper, fileSystem))
|
|
151
|
+
return "";
|
|
152
|
+
const id = sessionId(root, env, fileSystem);
|
|
153
|
+
if (!id)
|
|
154
|
+
return "";
|
|
155
|
+
const meta = readFile(path.join(root, "workspace/sessions", id, "meta.yaml"), fileSystem);
|
|
156
|
+
if (meta === undefined)
|
|
157
|
+
return "";
|
|
158
|
+
let value = "";
|
|
159
|
+
for (const line of meta.split("\n")) {
|
|
160
|
+
const firstField = line.replace(/^[ \t]+/, "").split(/[ \t]+/, 1)[0];
|
|
161
|
+
if (firstField !== "company_slug:")
|
|
162
|
+
continue;
|
|
163
|
+
value = line.replace(/^[^:]+:[ \t]*/, "").replace(/^"|"$/g, "");
|
|
164
|
+
break;
|
|
165
|
+
}
|
|
166
|
+
// The shell's command substitution strips trailing newlines, then tr removes
|
|
167
|
+
// all remaining whitespace and double quotes from the helper's output.
|
|
168
|
+
return value.replace(/[ \t\n\v\f\r"]/g, "");
|
|
169
|
+
}
|
|
170
|
+
function fleetIdentityPresent(rootPrefix, env, fileSystem) {
|
|
171
|
+
if (env.HQ_AGENT_WORKDIR || env.HQ_AGENT_COMPANY_DIR || env.HQ_AGENT_IDENTITY_FILE)
|
|
172
|
+
return true;
|
|
173
|
+
const paths = [
|
|
174
|
+
"/etc/hq-agent/identity.json",
|
|
175
|
+
"/etc/hq-agent/machine-creds.json",
|
|
176
|
+
rootPrefix ? `${rootPrefix.replace(/\/$/, "")}/var/lib/hq-agent/identity.json` : "/var/lib/hq-agent/identity.json",
|
|
177
|
+
"/var/lib/hq-agent/machine-creds.json",
|
|
178
|
+
];
|
|
179
|
+
return paths.some((filePath) => isFile(filePath, fileSystem));
|
|
180
|
+
}
|
|
181
|
+
function parseJson(text) {
|
|
182
|
+
try {
|
|
183
|
+
return JSON.parse(text);
|
|
184
|
+
}
|
|
185
|
+
catch {
|
|
186
|
+
return undefined;
|
|
187
|
+
}
|
|
188
|
+
}
|
|
189
|
+
function record(value) {
|
|
190
|
+
return value !== null && typeof value === "object" && !Array.isArray(value)
|
|
191
|
+
? value
|
|
192
|
+
: undefined;
|
|
193
|
+
}
|
|
194
|
+
function normalizeShellValue(value) {
|
|
195
|
+
return value.replace(/[ \t\n\v\f\r"]/g, "");
|
|
196
|
+
}
|
|
197
|
+
function defaultSlugFromJson(text, knownSlugs) {
|
|
198
|
+
const root = record(parseJson(text));
|
|
199
|
+
if (!root)
|
|
200
|
+
return "";
|
|
201
|
+
const candidate = root.defaultCompany;
|
|
202
|
+
const preference = candidate === null || candidate === undefined || candidate === false
|
|
203
|
+
? root
|
|
204
|
+
: record(candidate);
|
|
205
|
+
if (!preference)
|
|
206
|
+
return "";
|
|
207
|
+
if (preference.enabled !== true || (preference.source ?? "") === "disabled" || (preference.needsChoice ?? false) !== false)
|
|
208
|
+
return "";
|
|
209
|
+
if (typeof preference.slug !== "string")
|
|
210
|
+
return "";
|
|
211
|
+
const slug = normalizeShellValue(preference.slug);
|
|
212
|
+
return knownSlugs.has(slug) ? slug : "";
|
|
213
|
+
}
|
|
214
|
+
function validPreflight(text, slug, session, operation) {
|
|
215
|
+
const value = record(parseJson(text));
|
|
216
|
+
return Boolean(value && value.contractVersion === 1 && value.sessionId === session &&
|
|
217
|
+
value.clientOperationId === operation &&
|
|
218
|
+
typeof value.kind === "string" && ["queued", "needs_project", "needs_task", "bound"].includes(value.kind) &&
|
|
219
|
+
typeof value.classification === "string" && ["needs_project", "needs_task", "bound"].includes(value.classification) &&
|
|
220
|
+
typeof value.delivery === "string" && ["queued", "acked"].includes(value.delivery) && value.lifecycle === "open" &&
|
|
221
|
+
value.companySlug === slug &&
|
|
222
|
+
typeof value.companyUid === "string" && value.companyUid.length > 0);
|
|
223
|
+
}
|
|
224
|
+
function commandOnPath(name, env) {
|
|
225
|
+
const pathValue = env.PATH ?? "";
|
|
226
|
+
for (const directory of pathValue.split(path.delimiter)) {
|
|
227
|
+
const candidate = path.join(directory || ".", name);
|
|
228
|
+
try {
|
|
229
|
+
fs.accessSync(candidate, fs.constants.X_OK);
|
|
230
|
+
return true;
|
|
231
|
+
}
|
|
232
|
+
catch {
|
|
233
|
+
// `command -v` simply advances to the next PATH entry.
|
|
234
|
+
}
|
|
235
|
+
}
|
|
236
|
+
return false;
|
|
237
|
+
}
|
|
238
|
+
function runHq(args, env, timeoutMs, cwd) {
|
|
239
|
+
return new Promise((resolve) => {
|
|
240
|
+
let settled = false;
|
|
241
|
+
let stdout = "";
|
|
242
|
+
let child;
|
|
243
|
+
let timeoutKill;
|
|
244
|
+
let timedOut = false;
|
|
245
|
+
const signalGroup = (signal) => {
|
|
246
|
+
if (!child?.pid)
|
|
247
|
+
return;
|
|
248
|
+
try {
|
|
249
|
+
process.kill(-child.pid, signal);
|
|
250
|
+
}
|
|
251
|
+
catch (error) {
|
|
252
|
+
if (errorCode(error) !== "ESRCH")
|
|
253
|
+
finish({ code: 1, stdout });
|
|
254
|
+
}
|
|
255
|
+
};
|
|
256
|
+
const finish = (result) => {
|
|
257
|
+
if (settled)
|
|
258
|
+
return;
|
|
259
|
+
settled = true;
|
|
260
|
+
clearTimeout(timer);
|
|
261
|
+
if (timeoutKill)
|
|
262
|
+
clearTimeout(timeoutKill);
|
|
263
|
+
resolve(result);
|
|
264
|
+
};
|
|
265
|
+
const timer = setTimeout(() => {
|
|
266
|
+
timedOut = true;
|
|
267
|
+
if (child?.pid) {
|
|
268
|
+
signalGroup("SIGTERM");
|
|
269
|
+
// The shell wrapper returns shortly after its 2 second deadline. Do
|
|
270
|
+
// not wait for inherited stdio handles after killing the process group.
|
|
271
|
+
timeoutKill = setTimeout(() => {
|
|
272
|
+
signalGroup("SIGKILL");
|
|
273
|
+
finish({ code: 124, stdout });
|
|
274
|
+
}, 250);
|
|
275
|
+
}
|
|
276
|
+
else {
|
|
277
|
+
finish({ code: 124, stdout });
|
|
278
|
+
}
|
|
279
|
+
}, timeoutMs);
|
|
280
|
+
try {
|
|
281
|
+
child = spawn("hq", args, { cwd, detached: true, stdio: ["ignore", "pipe", "ignore"], env });
|
|
282
|
+
}
|
|
283
|
+
catch {
|
|
284
|
+
finish({ code: 127, stdout });
|
|
285
|
+
return;
|
|
286
|
+
}
|
|
287
|
+
child.stdout?.setEncoding("utf8");
|
|
288
|
+
child.stdout?.on("data", (chunk) => { stdout += chunk; });
|
|
289
|
+
child.once("error", () => finish({ code: 127, stdout }));
|
|
290
|
+
child.once("exit", (code, signal) => {
|
|
291
|
+
signalGroup("SIGTERM");
|
|
292
|
+
finish({ code: timedOut ? 124 : typeof code === "number" ? code : signal ? 128 : 1, stdout });
|
|
293
|
+
});
|
|
294
|
+
});
|
|
295
|
+
}
|
|
296
|
+
function output(company, source) {
|
|
297
|
+
return { code: 0, stdout: Buffer.from(`{"company":"${company}","source":"${source}"}\n`), stderr: Buffer.alloc(0) };
|
|
298
|
+
}
|
|
299
|
+
function parseArgs(args) {
|
|
300
|
+
let root = "";
|
|
301
|
+
let prompt = "";
|
|
302
|
+
let havePrompt = false;
|
|
303
|
+
for (let index = 0; index < args.length;) {
|
|
304
|
+
const arg = args[index];
|
|
305
|
+
if (arg === "--root") {
|
|
306
|
+
root = args[index + 1] ?? "";
|
|
307
|
+
index += Math.min(2, args.length - index);
|
|
308
|
+
}
|
|
309
|
+
else if (arg === "--prompt") {
|
|
310
|
+
prompt = args[index + 1] ?? "";
|
|
311
|
+
havePrompt = true;
|
|
312
|
+
index += Math.min(2, args.length - index);
|
|
313
|
+
}
|
|
314
|
+
else if (arg === "--help" || arg === "-h") {
|
|
315
|
+
return { root, prompt, havePrompt, help: true };
|
|
316
|
+
}
|
|
317
|
+
else {
|
|
318
|
+
index += 1;
|
|
319
|
+
}
|
|
320
|
+
}
|
|
321
|
+
return { root, prompt, havePrompt, help: false };
|
|
322
|
+
}
|
|
323
|
+
async function readStdin() {
|
|
324
|
+
const chunks = [];
|
|
325
|
+
for await (const chunk of process.stdin)
|
|
326
|
+
chunks.push(Buffer.isBuffer(chunk) ? chunk : Buffer.from(chunk));
|
|
327
|
+
return Buffer.concat(chunks).toString("utf8");
|
|
328
|
+
}
|
|
329
|
+
export async function resolveCompany(args, dependencies) {
|
|
330
|
+
const env = dependencies.env ?? process.env;
|
|
331
|
+
const fileSystem = dependencies.fs ?? fs;
|
|
332
|
+
const parsed = parseArgs(args);
|
|
333
|
+
if (parsed.help)
|
|
334
|
+
return { code: 0, stdout: Buffer.from(RESOLVE_COMPANY_HELP), stderr: Buffer.alloc(0) };
|
|
335
|
+
let prompt = parsed.prompt;
|
|
336
|
+
if (!parsed.havePrompt && dependencies.stdinIsTty !== true) {
|
|
337
|
+
prompt = dependencies.stdin ?? await readStdin();
|
|
338
|
+
}
|
|
339
|
+
const root = parsed.root || dependencies.hqRoot || env.HQ_ROOT || env.CLAUDE_PROJECT_DIR || dependencies.cwd;
|
|
340
|
+
const manifest = path.join(root, "companies/manifest.yaml");
|
|
341
|
+
if (!isFile(manifest, fileSystem))
|
|
342
|
+
return output("", "none");
|
|
343
|
+
const manifestResult = readManifest(manifest, fileSystem);
|
|
344
|
+
if (typeof manifestResult !== "string")
|
|
345
|
+
return manifestResult;
|
|
346
|
+
const manifestText = manifestResult;
|
|
347
|
+
const slugs = parseManifestSlugs(manifestText);
|
|
348
|
+
if (slugs.length === 0)
|
|
349
|
+
return output("", "none");
|
|
350
|
+
const knownSlugs = new Set(slugs);
|
|
351
|
+
const promptMatch = promptSlug(prompt, slugs);
|
|
352
|
+
if (promptMatch)
|
|
353
|
+
return output(promptMatch, "prompt");
|
|
354
|
+
const sessionMatch = sessionCompany(root, env, fileSystem);
|
|
355
|
+
if (sessionMatch && knownSlugs.has(sessionMatch))
|
|
356
|
+
return output(sessionMatch, "session");
|
|
357
|
+
if (fleetIdentityPresent(env.HQ_AGENT_ROOT_PREFIX ?? "", env, fileSystem))
|
|
358
|
+
return output("", "none");
|
|
359
|
+
// jq is intentionally no longer needed. The native route is always hosted by
|
|
360
|
+
// Node; the old shell route required `node`, `hq`, and `jq` on PATH.
|
|
361
|
+
if (!commandOnPath("hq", env))
|
|
362
|
+
return output("", "none");
|
|
363
|
+
const hqEnv = { ...EMPTY_ENV, ...env };
|
|
364
|
+
if (dependencies.hqRoot) {
|
|
365
|
+
hqEnv.HQ_ROOT = dependencies.hqRoot;
|
|
366
|
+
hqEnv.CLAUDE_PROJECT_DIR = dependencies.hqRoot;
|
|
367
|
+
}
|
|
368
|
+
const invoke = dependencies.spawnHq ?? runHq;
|
|
369
|
+
const defaultResponse = await invoke(["mesh", "context", "default", "get", "--json"], hqEnv, 2_000, root);
|
|
370
|
+
const defaultSlug = defaultSlugFromJson(defaultResponse.stdout, knownSlugs);
|
|
371
|
+
if (!defaultSlug)
|
|
372
|
+
return output("", "none");
|
|
373
|
+
const pid = dependencies.pid ?? process.pid;
|
|
374
|
+
const random = dependencies.random ?? Math.random;
|
|
375
|
+
const session = `resolve-default-${pid}-${Math.floor(random() * 32768)}`;
|
|
376
|
+
const operation = `resolve-default-op-${pid}-${Math.floor(random() * 32768)}`;
|
|
377
|
+
const observation = JSON.stringify({
|
|
378
|
+
contractVersion: 1,
|
|
379
|
+
clientOperationId: operation,
|
|
380
|
+
identity: { sessionId: session, harness: "hq-core", adapterVersion: "1" },
|
|
381
|
+
cwd: root,
|
|
382
|
+
hqRoot: root,
|
|
383
|
+
}).replaceAll("\u007f", "\\u007f");
|
|
384
|
+
const preflight = await invoke([
|
|
385
|
+
"mesh", "context", "reconcile", "--observation-json", observation, "--machine", "--offline",
|
|
386
|
+
], hqEnv, 2_000, root);
|
|
387
|
+
if (preflight.code === 0 && validPreflight(preflight.stdout, defaultSlug, session, operation)) {
|
|
388
|
+
return output(defaultSlug, "device_default");
|
|
389
|
+
}
|
|
390
|
+
return output("", "none");
|
|
391
|
+
}
|
|
392
|
+
export async function runResolveCompanyFast(args, stdin, cwd, hqRoot, _commandName, _hqRootArg, stdinReader = readStdin) {
|
|
393
|
+
const parsed = parseArgs(args);
|
|
394
|
+
const actualStdin = parsed.havePrompt || process.stdin.isTTY === true
|
|
395
|
+
? stdin
|
|
396
|
+
: stdin || await stdinReader();
|
|
397
|
+
const result = await resolveCompany(args, {
|
|
398
|
+
cwd,
|
|
399
|
+
hqRoot,
|
|
400
|
+
stdin: actualStdin,
|
|
401
|
+
stdinIsTty: process.stdin.isTTY === true,
|
|
402
|
+
});
|
|
403
|
+
process.stdout.write(result.stdout);
|
|
404
|
+
if (result.stderr.length)
|
|
405
|
+
process.stderr.write(result.stderr);
|
|
406
|
+
return result.code;
|
|
407
|
+
}
|
|
408
|
+
//# sourceMappingURL=core-resolve-company-native.js.map
|
package/dist/commands/core.js
CHANGED
|
@@ -37,6 +37,7 @@ import { registerCoreOntologyCommand } from "./core-ontology.js";
|
|
|
37
37
|
import { registerCoreKnowledgePrefsCommand } from "./core-knowledge-prefs.js";
|
|
38
38
|
import { registerCorePolicyCommand } from "./core-policy.js";
|
|
39
39
|
import { registerCoreJobsNativeCommand } from "./core-job-native.js";
|
|
40
|
+
import { registerCoreResolveCompanyNativeCommand } from "./core-resolve-company-command.js";
|
|
40
41
|
import { registerCoreCheckpointGateSettingCommand } from "./core-checkpoint-gate-setting.js";
|
|
41
42
|
import { registerCoreAuditLogNative } from "./core-audit-log-native.js";
|
|
42
43
|
import { registerCoreGitPackExtensionCheckNative } from "./core-git-pack-extension-check-native.js";
|
|
@@ -218,6 +219,7 @@ export const SCAFFOLD_COMMANDS = [
|
|
|
218
219
|
* direct `hq core` command.
|
|
219
220
|
*/
|
|
220
221
|
export const SCAFFOLD_ONLY_ASSETS = [
|
|
222
|
+
{ asset: "core/scripts/resolve-company.sh", root: "live", summary: "Rollback companion for native resolve-company" },
|
|
221
223
|
{ asset: "core/scripts/jobs-schedule-parse.sh", root: "live", summary: "Rollback companion for native jobs schedule-parse" },
|
|
222
224
|
{ asset: "core/scripts/jobs-validate.sh", root: "live", summary: "Rollback companion for native jobs validate" },
|
|
223
225
|
{ asset: "core/scripts/outpost-jobs-reconcile.sh", root: "live", summary: "Rollback companion for native jobs reconcile" },
|
|
@@ -365,6 +367,7 @@ export function registerCoreCommands(program) {
|
|
|
365
367
|
registerCoreKnowledgePrefsCommand(core);
|
|
366
368
|
registerCorePolicyCommand(core);
|
|
367
369
|
registerCoreJobsNativeCommand(core);
|
|
370
|
+
registerCoreResolveCompanyNativeCommand(core);
|
|
368
371
|
registerCoreAuditLogNative(core);
|
|
369
372
|
registerCoreGitPackExtensionCheckNative(core);
|
|
370
373
|
registerCorePinNative(core);
|
package/dist/fast-scaffold.js
CHANGED
|
@@ -144,6 +144,18 @@ export const FAST_SCAFFOLD_SCRIPTS = [
|
|
|
144
144
|
shellCwd: "caller",
|
|
145
145
|
shellHqRoot: name === "reconcile" ? "reconcile-default" : "none",
|
|
146
146
|
})),
|
|
147
|
+
{
|
|
148
|
+
name: "resolve-company",
|
|
149
|
+
kind: "native",
|
|
150
|
+
modulePath: "./commands/core-resolve-company-native.js",
|
|
151
|
+
exportName: "runResolveCompanyFast",
|
|
152
|
+
flagKey: "hq-core-native-fast-route",
|
|
153
|
+
familyFlagKey: "core-native-utilities",
|
|
154
|
+
asset: "core/scripts/resolve-company.sh",
|
|
155
|
+
root: "live",
|
|
156
|
+
shellCwd: "live",
|
|
157
|
+
shellHqRoot: "live",
|
|
158
|
+
},
|
|
147
159
|
...["resolve", "pickup", "repo", "secrets", "transfer"].map((name) => ({
|
|
148
160
|
name: `delegate ${name}`,
|
|
149
161
|
kind: "native",
|