@ainova-systems/intelligence 0.11.8 → 0.11.9
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/cli/commands/init.sh +1 -0
- package/cli/commands/status.sh +13 -3
- package/cli/commands/update.sh +1 -0
- package/cli/internal/check.sh +22 -3
- package/cli/internal/convert-legacy.sh +5 -4
- package/cli/internal/package-list.sh +14 -4
- package/cli/internal/restore.sh +1 -14
- package/cli/lib/cli-common.sh +50 -4
- package/cli/lib/lockfile.sh +72 -28
- package/cli/lib/manifest.sh +30 -65
- package/cli/lib/qmap.awk +191 -0
- package/cli/lib/registry.sh +1 -3
- package/engine/ENGINE_SHA +1 -1
- package/engine/VERSION +1 -1
- package/package.json +1 -1
package/cli/commands/init.sh
CHANGED
|
@@ -123,6 +123,7 @@ case "$IP_MODE" in
|
|
|
123
123
|
[ "$force" -eq 0 ] || die "--force applies only when converting a legacy Intelligence Sync project"
|
|
124
124
|
if [ "$preview" -eq 1 ]; then
|
|
125
125
|
check_version_compat "$IP_ROOT/intelligence.yaml"
|
|
126
|
+
validate_project_lock "$IP_ROOT"
|
|
126
127
|
echo "project: Intelligence CLI at $IP_ROOT"
|
|
127
128
|
if project_stamped_ahead "$IP_ROOT"; then
|
|
128
129
|
echo " project schema $(read_schema_version "$IP_ROOT/intelligence.yaml") is ahead of engine $(bundled_engine_version) — left as is (update the CLI)"
|
package/cli/commands/status.sh
CHANGED
|
@@ -12,22 +12,32 @@ esac
|
|
|
12
12
|
detect_project
|
|
13
13
|
case "$IP_MODE" in
|
|
14
14
|
cli)
|
|
15
|
+
lock_valid=1
|
|
15
16
|
echo "Project: $IP_ROOT (Intelligence project)"
|
|
16
17
|
echo "Manifest: intelligence.yaml (schema_version $(read_schema_version "$IP_ROOT/intelligence.yaml"))"
|
|
17
|
-
if
|
|
18
|
+
if ! check_project_lock "$IP_ROOT"; then
|
|
19
|
+
lock_valid=0
|
|
20
|
+
echo "Lockfile: INVALID (locked state unchecked)"
|
|
21
|
+
elif [ -f "$IP_ROOT/intelligence.lock" ]; then
|
|
18
22
|
echo "Lockfile: intelligence.lock"
|
|
19
23
|
elif [ -n "$(qmap_keys "$IP_ROOT/intelligence.yaml" "packages")" ]; then
|
|
20
24
|
echo "Lockfile: MISSING (manifest declares packages)"
|
|
21
25
|
else
|
|
22
26
|
echo "Lockfile: none (no packages added yet)"
|
|
23
27
|
fi
|
|
24
|
-
sync_locked="
|
|
25
|
-
if [
|
|
28
|
+
sync_locked=""
|
|
29
|
+
if [ "$lock_valid" -eq 1 ]; then
|
|
30
|
+
sync_locked="$(qmap_field "$IP_ROOT/intelligence.lock" "packages" "$SYNC_PKG_NAME" "resolved")"
|
|
31
|
+
fi
|
|
32
|
+
if [ "$lock_valid" -eq 0 ]; then
|
|
33
|
+
echo "Content: unchecked"
|
|
34
|
+
elif [ -n "$sync_locked" ]; then
|
|
26
35
|
echo "Content: $SYNC_PKG_NAME at $sync_locked"
|
|
27
36
|
else
|
|
28
37
|
echo "Content: no $SYNC_PKG_NAME (bare setup)"
|
|
29
38
|
fi
|
|
30
39
|
echo "Engine: $(bundled_engine_version) (bundled with the CLI)"
|
|
40
|
+
[ "$lock_valid" -eq 1 ] || exit 1
|
|
31
41
|
;;
|
|
32
42
|
legacy)
|
|
33
43
|
echo "Project: $IP_ROOT (legacy Intelligence Sync)"
|
package/cli/commands/update.sh
CHANGED
package/cli/internal/check.sh
CHANGED
|
@@ -31,6 +31,12 @@ esac
|
|
|
31
31
|
echo "project: $IP_ROOT"
|
|
32
32
|
manifest="$IP_ROOT/intelligence.yaml"
|
|
33
33
|
lock="$IP_ROOT/intelligence.lock"
|
|
34
|
+
lock_valid=1
|
|
35
|
+
if ! check_project_lock "$IP_ROOT"; then
|
|
36
|
+
problems=$((problems + 1))
|
|
37
|
+
lock_valid=0
|
|
38
|
+
note "locked package identities cannot be inspected; continuing independent project checks"
|
|
39
|
+
fi
|
|
34
40
|
|
|
35
41
|
stamp="$(read_schema_version "$manifest")"
|
|
36
42
|
eng="$(bundled_engine_version)"
|
|
@@ -50,7 +56,10 @@ fi
|
|
|
50
56
|
|
|
51
57
|
# The engine-content package must sit at the bundled engine version — the
|
|
52
58
|
# content documents exactly the scripts the CLI executes.
|
|
53
|
-
sync_locked="
|
|
59
|
+
sync_locked=""
|
|
60
|
+
if [ "$lock_valid" -eq 1 ]; then
|
|
61
|
+
sync_locked="$(qmap_field "$lock" "packages" "$SYNC_PKG_NAME" "resolved")"
|
|
62
|
+
fi
|
|
54
63
|
if [ -n "$sync_locked" ]; then
|
|
55
64
|
if [ "${sync_locked#v}" = "$eng" ]; then
|
|
56
65
|
ok "$SYNC_PKG_NAME at $sync_locked (matches the engine)"
|
|
@@ -70,6 +79,10 @@ fi
|
|
|
70
79
|
while IFS= read -r name; do
|
|
71
80
|
[ -n "$name" ] || continue
|
|
72
81
|
assert_valid_pkg_name "$name"
|
|
82
|
+
if [ "$lock_valid" -eq 0 ]; then
|
|
83
|
+
note "$name: locked state unchecked"
|
|
84
|
+
continue
|
|
85
|
+
fi
|
|
73
86
|
if [ -z "$(qmap_field "$lock" "packages" "$name" "url")" ]; then
|
|
74
87
|
warn "$name is in the manifest but not in intelligence.lock — run 'intelligence init'"
|
|
75
88
|
else
|
|
@@ -143,7 +156,7 @@ while IFS= read -r target; do
|
|
|
143
156
|
done < <(target_names "$manifest")
|
|
144
157
|
|
|
145
158
|
# Lock orphans.
|
|
146
|
-
if [ -f "$lock" ]; then
|
|
159
|
+
if [ "$lock_valid" -eq 1 ] && [ -f "$lock" ]; then
|
|
147
160
|
while IFS= read -r name; do
|
|
148
161
|
[ -n "$name" ] || continue
|
|
149
162
|
found=0
|
|
@@ -165,7 +178,13 @@ for section in rules agents skills; do
|
|
|
165
178
|
if [ ! -d "$IP_ROOT/$src" ]; then
|
|
166
179
|
case "$src" in
|
|
167
180
|
# Store content is restorable state: absent means un-installed.
|
|
168
|
-
.intelligence/*)
|
|
181
|
+
.intelligence/*)
|
|
182
|
+
if [ "$lock_valid" -eq 1 ]; then
|
|
183
|
+
warn "sources.$section '$src' missing — run 'intelligence sync'"
|
|
184
|
+
else
|
|
185
|
+
warn "sources.$section '$src' missing — resolve the lock problem first"
|
|
186
|
+
fi
|
|
187
|
+
;;
|
|
169
188
|
# A project-owned dir that does not exist yet is a legitimate
|
|
170
189
|
# state — a project consuming only packages authors nothing of
|
|
171
190
|
# its own — and the engine simply skips it. Declared up front so
|
|
@@ -183,7 +183,9 @@ while IFS="$LOCK_SEP" read -r pack name url ref mirror; do
|
|
|
183
183
|
mkdir -p "$dest"
|
|
184
184
|
cp -R "$root/$mirror/." "$dest/"
|
|
185
185
|
if [ -f "$dest/.pack" ]; then
|
|
186
|
-
sha="$(awk -F= '$1 == "sha" {print $2; exit}' "$dest/.pack")"
|
|
186
|
+
sha="$(awk -F= '{ sub(/\r$/, "") } $1 == "sha" {print $2; exit}' "$dest/.pack")"
|
|
187
|
+
[[ "$sha" =~ ^[0-9a-f]{40}$ || "$sha" =~ ^[0-9a-f]{64}$ ]] \
|
|
188
|
+
|| die "pack '$pack' mirror '$mirror' has a missing or invalid recorded commit SHA in .pack — legacy project unchanged; restore its trusted ownership stamp from history or refresh the mirror with its archived engine, then rerun 'intelligence init'"
|
|
187
189
|
rm -f "$dest/.pack"
|
|
188
190
|
fi
|
|
189
191
|
echo " pack '$pack' -> $name (from mirror $mirror)"
|
|
@@ -191,9 +193,8 @@ while IFS="$LOCK_SEP" read -r pack name url ref mirror; do
|
|
|
191
193
|
sha="$(fetch_package "$url" "$ref" "" "$dest")"
|
|
192
194
|
echo " pack '$pack' -> $name (fetched)"
|
|
193
195
|
fi
|
|
194
|
-
|
|
195
|
-
|
|
196
|
-
fi
|
|
196
|
+
# A ref currently advertised by the remote cannot prove which commit
|
|
197
|
+
# produced an existing mirror. Only its recorded identity can do that.
|
|
197
198
|
[ -n "$ref" ] || ref="HEAD"
|
|
198
199
|
printf '%s\037%s\037%s\037%s\037%s\037%s\n' \
|
|
199
200
|
"$name" "" "$url" "" "$ref" "$sha" >> "$lock_rows"
|
|
@@ -6,6 +6,8 @@ source "$CLI_DIR/lib/cli-common.sh"
|
|
|
6
6
|
require_cli_project
|
|
7
7
|
manifest="$IP_ROOT/intelligence.yaml"
|
|
8
8
|
lock="$IP_ROOT/intelligence.lock"
|
|
9
|
+
lock_valid=1
|
|
10
|
+
if ! check_project_lock "$IP_ROOT"; then lock_valid=0; fi
|
|
9
11
|
|
|
10
12
|
count=0
|
|
11
13
|
while IFS= read -r name; do
|
|
@@ -13,9 +15,14 @@ while IFS= read -r name; do
|
|
|
13
15
|
count=$((count + 1))
|
|
14
16
|
range="$(qmap_field "$manifest" "packages" "$name" "version")"
|
|
15
17
|
ref="$(qmap_field "$manifest" "packages" "$name" "ref")"
|
|
16
|
-
resolved="
|
|
17
|
-
|
|
18
|
-
|
|
18
|
+
resolved="" sha=""
|
|
19
|
+
if [ "$lock_valid" -eq 1 ]; then
|
|
20
|
+
resolved="$(qmap_field "$lock" "packages" "$name" "resolved")"
|
|
21
|
+
sha="$(qmap_field "$lock" "packages" "$name" "sha")"
|
|
22
|
+
fi
|
|
23
|
+
if [ "$lock_valid" -eq 0 ]; then
|
|
24
|
+
state="invalid lock — locked state unchecked"
|
|
25
|
+
elif [ ! -f "$lock" ]; then
|
|
19
26
|
state="lock missing — restore committed intelligence.lock"
|
|
20
27
|
elif [ -d "$IP_ROOT/.intelligence/packages/$name" ]; then
|
|
21
28
|
state="installed"
|
|
@@ -30,5 +37,8 @@ while IFS= read -r name; do
|
|
|
30
37
|
done < <(qmap_keys "$manifest" "packages")
|
|
31
38
|
|
|
32
39
|
if [ "$count" -eq 0 ]; then
|
|
33
|
-
|
|
40
|
+
if [ "$lock_valid" -eq 1 ]; then
|
|
41
|
+
echo "No packages. Add one: intelligence package add @ainova-systems/core"
|
|
42
|
+
fi
|
|
34
43
|
fi
|
|
44
|
+
[ "$lock_valid" -eq 1 ] || exit 1
|
package/cli/internal/restore.sh
CHANGED
|
@@ -21,6 +21,7 @@ done
|
|
|
21
21
|
require_cli_project
|
|
22
22
|
manifest="$IP_ROOT/intelligence.yaml"
|
|
23
23
|
lock="$IP_ROOT/intelligence.lock"
|
|
24
|
+
validate_project_lock "$IP_ROOT" --restore
|
|
24
25
|
|
|
25
26
|
# --frozen is the reproducibility contract: the lock must agree with the
|
|
26
27
|
# manifest on the package SET and on every requested-intent field before a
|
|
@@ -50,20 +51,6 @@ if [ "$frozen" -eq 1 ]; then
|
|
|
50
51
|
done < <(qmap_keys "$lock" "packages")
|
|
51
52
|
fi
|
|
52
53
|
|
|
53
|
-
# Check every required commit identity before restoring any package. Only the
|
|
54
|
-
# matching bundled content may carry the historical empty development-build SHA.
|
|
55
|
-
while IFS="$LOCK_SEP" read -r name _requested url path resolved sha; do
|
|
56
|
-
[ -n "$name" ] || continue
|
|
57
|
-
assert_valid_pkg_name "$name"
|
|
58
|
-
assert_safe_source_url "$url"
|
|
59
|
-
assert_safe_ref "$resolved"
|
|
60
|
-
if [ -z "$sha" ] && is_bundle_source "$url" "$resolved" "$path"; then
|
|
61
|
-
continue
|
|
62
|
-
fi
|
|
63
|
-
[[ "$sha" =~ ^[0-9a-f]{40}$ || "$sha" =~ ^[0-9a-f]{64}$ ]] \
|
|
64
|
-
|| die "locked restore: $name has a missing or invalid commit SHA — restore a valid intelligence.lock"
|
|
65
|
-
done < <(lock_to_tsv "$lock")
|
|
66
|
-
|
|
67
54
|
# Manifest packages missing from the lock: resolve them now (npm install
|
|
68
55
|
# semantics). Under --frozen the drift gate above has already refused.
|
|
69
56
|
missing=""
|
package/cli/lib/cli-common.sh
CHANGED
|
@@ -14,7 +14,7 @@ source "$IS_ENGINE_DIR/lib/contract.sh"
|
|
|
14
14
|
source "$IS_ENGINE_DIR/lib/adapter-contract.sh"
|
|
15
15
|
|
|
16
16
|
die() {
|
|
17
|
-
echo "ERROR: $*" >&2
|
|
17
|
+
echo "ERROR: ${CLI_INPUT_CONTEXT:-}$*" >&2
|
|
18
18
|
exit 1
|
|
19
19
|
}
|
|
20
20
|
|
|
@@ -30,7 +30,7 @@ assert_safe_source_url() {
|
|
|
30
30
|
local url="$1"
|
|
31
31
|
case "$url" in
|
|
32
32
|
""|-*) die "unsafe source url '$url' — option-shaped or empty" ;;
|
|
33
|
-
*[\"\'\ ]*) die "unsafe source url '$url' — quotes or
|
|
33
|
+
*[\"\'\ ]*|*[[:cntrl:]]*) die "unsafe source url '$url' — quotes or whitespace" ;;
|
|
34
34
|
https://*|http://*|ssh://*|git://*|file://*) ;;
|
|
35
35
|
*@*:*) ;;
|
|
36
36
|
*) die "unsafe source url '$url' — allowed: https, http, ssh, git, file, or user@host:path" ;;
|
|
@@ -43,7 +43,17 @@ assert_safe_ref() {
|
|
|
43
43
|
[ -z "$ref" ] && return 0
|
|
44
44
|
case "$ref" in
|
|
45
45
|
-*) die "unsafe git ref '$ref' — option-shaped" ;;
|
|
46
|
-
*[\"\'\ \\]*) die "unsafe git ref '$ref'" ;;
|
|
46
|
+
*[\"\'\ \\]*|*[[:cntrl:]]*) die "unsafe git ref '$ref'" ;;
|
|
47
|
+
esac
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
# Lexical package-subdirectory validation shared by acquisition and lock
|
|
51
|
+
# preflight. Physical containment of acquired content is a separate boundary.
|
|
52
|
+
assert_safe_package_path() {
|
|
53
|
+
# Preserve literal Git paths, but also guard Windows separator semantics.
|
|
54
|
+
local normalized="${1//\\//}"
|
|
55
|
+
case "$normalized" in
|
|
56
|
+
*..*|/*|[A-Za-z]:*|*[\"\']*|*[[:cntrl:]]*) die "unsafe path '$1'" ;;
|
|
47
57
|
esac
|
|
48
58
|
}
|
|
49
59
|
|
|
@@ -160,7 +170,9 @@ default_target_output() {
|
|
|
160
170
|
}
|
|
161
171
|
|
|
162
172
|
bundled_engine_version() {
|
|
163
|
-
|
|
173
|
+
local version
|
|
174
|
+
version="$(< "$IS_ENGINE_DIR/VERSION")"
|
|
175
|
+
printf '%s' "${version//[$' \t\r\n']/}"
|
|
164
176
|
}
|
|
165
177
|
|
|
166
178
|
# --- The sync package's manifest/lock plumbing ---------------------------
|
|
@@ -297,6 +309,7 @@ project_has_packages() {
|
|
|
297
309
|
ensure_project_current() {
|
|
298
310
|
local root="$1" explicit="${2:-}" manifest="$1/intelligence.yaml" stamp eng
|
|
299
311
|
check_version_compat "$manifest" || return $?
|
|
312
|
+
validate_project_lock "$root" || return $?
|
|
300
313
|
if project_has_packages "$root" && [ ! -f "$root/intelligence.lock" ]; then
|
|
301
314
|
die "manifest declares packages but intelligence.lock is absent — restore the committed lock before running project lifecycle commands"
|
|
302
315
|
fi
|
|
@@ -310,6 +323,39 @@ ensure_project_current() {
|
|
|
310
323
|
bash "$CLI_DIR/internal/align-project.sh" --no-sync
|
|
311
324
|
}
|
|
312
325
|
|
|
326
|
+
# A present lock is validated even when there is no missing store to restore.
|
|
327
|
+
# Keep this before alignment: repairing the sync pin must not rewrite bad input.
|
|
328
|
+
validate_project_lock() {
|
|
329
|
+
local lock="$1/intelligence.lock" output line rc=0
|
|
330
|
+
if [ -e "$lock" ] || [ -L "$lock" ]; then
|
|
331
|
+
output="$(lock_validate "$lock" "${2---metadata}" "$1/intelligence.yaml" 2>&1)" || rc=$?
|
|
332
|
+
while IFS= read -r line; do
|
|
333
|
+
[ -n "$line" ] || continue
|
|
334
|
+
case "$line" in
|
|
335
|
+
WARNING:*)
|
|
336
|
+
case "${IS_LOCK_WARNINGS:-}" in *"$line"*) continue ;; esac
|
|
337
|
+
# Suppress only duplicate messages across lifecycle children;
|
|
338
|
+
# the lock itself is always parsed and validated again.
|
|
339
|
+
export IS_LOCK_WARNINGS="${IS_LOCK_WARNINGS:-}$line"$'\n'
|
|
340
|
+
;;
|
|
341
|
+
esac
|
|
342
|
+
echo "$line" >&2
|
|
343
|
+
done <<< "$output"
|
|
344
|
+
if [ "$rc" -ne 0 ]; then
|
|
345
|
+
echo "ERROR: invalid intelligence.lock — recovery: https://github.com/ainova-systems/intelligence/blob/main/docs/cli.md#recovering-a-lock" >&2
|
|
346
|
+
return 1
|
|
347
|
+
fi
|
|
348
|
+
fi
|
|
349
|
+
}
|
|
350
|
+
|
|
351
|
+
# Read-only diagnosis must distinguish usable installed metadata from metadata
|
|
352
|
+
# which could actually restore a missing store.
|
|
353
|
+
check_project_lock() {
|
|
354
|
+
local mode=--metadata
|
|
355
|
+
if project_store_missing "$1"; then mode=--restore; fi
|
|
356
|
+
validate_project_lock "$1" "$mode"
|
|
357
|
+
}
|
|
358
|
+
|
|
313
359
|
project_store_missing() {
|
|
314
360
|
local root="$1" manifest="$1/intelligence.yaml" name src
|
|
315
361
|
while IFS= read -r name; do
|
package/cli/lib/lockfile.sh
CHANGED
|
@@ -10,38 +10,74 @@
|
|
|
10
10
|
LOCKFILE_VERSION=1
|
|
11
11
|
LOCK_SEP=$'\x1f'
|
|
12
12
|
|
|
13
|
-
#
|
|
14
|
-
#
|
|
15
|
-
|
|
13
|
+
# lock_validate <lock> [--metadata|--restore] [manifest] — read-only preflight. A subshell
|
|
14
|
+
# contains the fail-fast input guards so status can report a failed verdict too.
|
|
15
|
+
lock_validate() (
|
|
16
|
+
local lock="$1" mode="${2---metadata}" manifest="${3:-}" count=0
|
|
17
|
+
local kind name requested url path resolved sha line rows current
|
|
18
|
+
local -x CLI_INPUT_CONTEXT=""
|
|
19
|
+
[ "$#" -le 3 ] || die "internal: too many lock validation arguments"
|
|
20
|
+
case "$mode" in
|
|
21
|
+
--metadata|--restore) ;;
|
|
22
|
+
*) die "internal: unknown lock validation mode '$mode'" ;;
|
|
23
|
+
esac
|
|
24
|
+
# The same tokenizer supplies ordinary lock rows and strict metadata. Buffer
|
|
25
|
+
# before consumption: a failed parser must never yield a partial package set.
|
|
26
|
+
rows="$(_qmap_read lock "$lock" packages '' '' "$LOCKFILE_VERSION")" || return $?
|
|
27
|
+
current="$(bundled_engine_version)"
|
|
28
|
+
while IFS="$LOCK_SEP" read -r kind name requested url path resolved sha line; do
|
|
29
|
+
case "$kind" in
|
|
30
|
+
V)
|
|
31
|
+
[[ "$requested" =~ ^[0-9]+\.[0-9]+\.[0-9]+$ ]] \
|
|
32
|
+
|| die "$lock has missing or invalid engine_version"
|
|
33
|
+
continue
|
|
34
|
+
;;
|
|
35
|
+
R) ;;
|
|
36
|
+
*) die "internal: unexpected lock record '$kind'" ;;
|
|
37
|
+
esac
|
|
38
|
+
count=$((count + 1))
|
|
39
|
+
CLI_INPUT_CONTEXT="$lock:$line: package $name, field name: "
|
|
40
|
+
assert_valid_pkg_name "$name"
|
|
41
|
+
CLI_INPUT_CONTEXT="$lock:$line: package $name, field url: "
|
|
42
|
+
assert_safe_source_url "$url"
|
|
43
|
+
CLI_INPUT_CONTEXT="$lock:$line: package $name, field resolved: "
|
|
44
|
+
[ -n "$resolved" ] || die "locked restore: $name has a missing resolved ref"
|
|
45
|
+
assert_safe_ref "$resolved"
|
|
46
|
+
CLI_INPUT_CONTEXT="$lock:$line: package $name, field path: "
|
|
47
|
+
assert_safe_package_path "$path"
|
|
48
|
+
CLI_INPUT_CONTEXT="$lock:$line: package $name, field sha: "
|
|
49
|
+
if [ -z "$sha" ]; then
|
|
50
|
+
if [ "$url" = "$SYNC_PKG_URL" ] && [ "$path" = "$SYNC_PKG_PATH" ] \
|
|
51
|
+
&& [ "$resolved" = "v$current" ]; then
|
|
52
|
+
continue
|
|
53
|
+
fi
|
|
54
|
+
# engine_version records the writer CLI, which can differ from the
|
|
55
|
+
# preserved bundle pin after a same-major older CLI adds a package.
|
|
56
|
+
# Versioned bundle metadata admits alignment/installed content only;
|
|
57
|
+
# it must never authorize an unpinned cross-version acquisition.
|
|
58
|
+
if [ "$mode" = "--metadata" ] && [ "$url" = "$SYNC_PKG_URL" ] \
|
|
59
|
+
&& [ "$path" = "$SYNC_PKG_PATH" ] && [[ "$resolved" =~ ^v[0-9]+\.[0-9]+\.[0-9]+$ ]] \
|
|
60
|
+
&& [ "${resolved%%.*}" = "v${current%%.*}" ]; then
|
|
61
|
+
echo "WARNING: $name at $resolved has no locked commit SHA; commit verification is unavailable for this development-bundle metadata" >&2
|
|
62
|
+
continue
|
|
63
|
+
fi
|
|
64
|
+
fi
|
|
65
|
+
[[ "$sha" =~ ^[0-9a-f]{40}$ || "$sha" =~ ^[0-9a-f]{64}$ ]] \
|
|
66
|
+
|| die "locked restore: $name has a missing or invalid commit SHA — restore a valid intelligence.lock"
|
|
67
|
+
done <<< "$rows"
|
|
68
|
+
if [ "$count" -eq 0 ] && [ -n "$manifest" ] && [ -n "$(qmap_keys "$manifest" packages)" ]; then
|
|
69
|
+
die "$lock: packages is empty but the manifest declares packages — restore their recorded identities"
|
|
70
|
+
fi
|
|
71
|
+
)
|
|
72
|
+
|
|
73
|
+
# top_scalar <file> <key> — one top-level scalar through the shared tokenizer.
|
|
16
74
|
top_scalar() {
|
|
17
|
-
|
|
18
|
-
awk -v key="$2" '
|
|
19
|
-
{ sub(/\r$/, "") }
|
|
20
|
-
$0 ~ "^" key ":" {
|
|
21
|
-
v = substr($0, length(key) + 2)
|
|
22
|
-
sub(/^[ \t]+/, "", v)
|
|
23
|
-
if (v ~ /^"/) { v = substr(v, 2); q = index(v, "\""); if (q > 0) v = substr(v, 1, q - 1) }
|
|
24
|
-
else { sub(/[ \t]+#.*$/, "", v); sub(/[ \t]+$/, "", v) }
|
|
25
|
-
print v
|
|
26
|
-
exit
|
|
27
|
-
}
|
|
28
|
-
' "$1"
|
|
75
|
+
_qmap_read top "$1" '' "$2"
|
|
29
76
|
}
|
|
30
77
|
|
|
31
78
|
# lock_to_tsv <lock> — every locked package as one row.
|
|
32
79
|
lock_to_tsv() {
|
|
33
|
-
|
|
34
|
-
[ -f "$lock" ] || return 0
|
|
35
|
-
while IFS= read -r name; do
|
|
36
|
-
[ -n "$name" ] || continue
|
|
37
|
-
printf '%s\037%s\037%s\037%s\037%s\037%s\n' \
|
|
38
|
-
"$name" \
|
|
39
|
-
"$(qmap_field "$lock" "packages" "$name" "requested")" \
|
|
40
|
-
"$(qmap_field "$lock" "packages" "$name" "url")" \
|
|
41
|
-
"$(qmap_field "$lock" "packages" "$name" "path")" \
|
|
42
|
-
"$(qmap_field "$lock" "packages" "$name" "resolved")" \
|
|
43
|
-
"$(qmap_field "$lock" "packages" "$name" "sha")"
|
|
44
|
-
done < <(qmap_keys "$lock" "packages")
|
|
80
|
+
_qmap_read rows "$1" packages
|
|
45
81
|
}
|
|
46
82
|
|
|
47
83
|
# lock_write_from_tsv <lock> <tsv-file> — rebuild the lock. Entries are
|
|
@@ -58,7 +94,15 @@ lock_write_from_tsv() {
|
|
|
58
94
|
# them (registry-supplied url/path included) must be escaped, or it
|
|
59
95
|
# would break or alias the neighbouring field.
|
|
60
96
|
awk -F"$LOCK_SEP" '
|
|
61
|
-
function esc(s
|
|
97
|
+
function esc(s, i, c, out) {
|
|
98
|
+
out = ""
|
|
99
|
+
for (i = 1; i <= length(s); i++) {
|
|
100
|
+
c = substr(s, i, 1)
|
|
101
|
+
if (c == "\\" || c == "\"") out = out "\\"
|
|
102
|
+
out = out c
|
|
103
|
+
}
|
|
104
|
+
return out
|
|
105
|
+
}
|
|
62
106
|
NF >= 6 {
|
|
63
107
|
print " \"" esc($1) "\":"
|
|
64
108
|
if ($2 != "") print " requested: \"" esc($2) "\""
|
package/cli/lib/manifest.sh
CHANGED
|
@@ -87,7 +87,8 @@ target_set_enabled() {
|
|
|
87
87
|
case "$enabled" in true|false) ;; *) die "internal: invalid target state '$enabled'" ;; esac
|
|
88
88
|
target_exists "$file" "$target" && target_missing=0
|
|
89
89
|
output="$(_yq_esc "$output")"
|
|
90
|
-
_qmap_stage "$file" -v target="$target" -v enabled="$enabled" -v
|
|
90
|
+
QMAP_EDIT_VALUE="$output" _qmap_stage "$file" -v target="$target" -v enabled="$enabled" -v target_missing="$target_missing" '
|
|
91
|
+
BEGIN { output = ENVIRON["QMAP_EDIT_VALUE"] }
|
|
91
92
|
function entry() { return " " target ": { enabled: " enabled ", output: \"" output "\" }" }
|
|
92
93
|
function insert_missing_target() {
|
|
93
94
|
if (in_targets && target_missing && !target_seen) { print entry(); target_seen = 1 }
|
|
@@ -154,71 +155,33 @@ target_set_enabled() {
|
|
|
154
155
|
'
|
|
155
156
|
}
|
|
156
157
|
|
|
157
|
-
#
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
}
|
|
171
|
-
' "$1"
|
|
158
|
+
# _qmap_read <mode> <file> <block> [key] [field] [expected-lock-version]
|
|
159
|
+
# All scalar reads and strict lock records share one tokenizer. Environment
|
|
160
|
+
# transport preserves literal backslashes that awk -v would interpret again.
|
|
161
|
+
_qmap_read() {
|
|
162
|
+
local mode="$1" file="$2"
|
|
163
|
+
if [ ! -f "$file" ] || [ ! -r "$file" ]; then
|
|
164
|
+
case "$mode" in
|
|
165
|
+
lock|validate) echo "cannot read $file" >&2; return 1 ;;
|
|
166
|
+
*) return 0 ;;
|
|
167
|
+
esac
|
|
168
|
+
fi
|
|
169
|
+
QMAP_MODE="$mode" QMAP_FILE="$file" QMAP_BLOCK="${3:-}" \
|
|
170
|
+
QMAP_KEY="${4:-}" QMAP_FIELD="${5:-}" QMAP_EXPECTED_VERSION="${6:-}" \
|
|
171
|
+
LC_ALL=C awk -f "${BASH_SOURCE[0]%/*}/qmap.awk" < "$file"
|
|
172
172
|
}
|
|
173
173
|
|
|
174
|
-
#
|
|
175
|
-
|
|
176
|
-
qmap_field() {
|
|
177
|
-
[ -f "$1" ] || return 0
|
|
178
|
-
awk -v block="$2" -v key="$3" -v field="$4" '
|
|
179
|
-
{ sub(/\r$/, "") }
|
|
180
|
-
$0 ~ "^" block ":[ \t]*$" { inb = 1; next }
|
|
181
|
-
inb && /^[^ #]/ { inb = 0; ink = 0 }
|
|
182
|
-
inb && /^ "/ {
|
|
183
|
-
s = substr($0, 4); q = index(s, "\"")
|
|
184
|
-
ink = (q > 0 && substr(s, 1, q - 1) == key)
|
|
185
|
-
next
|
|
186
|
-
}
|
|
187
|
-
inb && ink && /^ [A-Za-z_]/ {
|
|
188
|
-
line = $0
|
|
189
|
-
sub(/^ /, "", line)
|
|
190
|
-
c = index(line, ":")
|
|
191
|
-
if (c == 0) next
|
|
192
|
-
if (substr(line, 1, c - 1) != field) next
|
|
193
|
-
v = substr(line, c + 1)
|
|
194
|
-
sub(/^[ \t]+/, "", v)
|
|
195
|
-
if (v ~ /^"/) { v = substr(v, 2); q2 = index(v, "\""); if (q2 > 0) v = substr(v, 1, q2 - 1) }
|
|
196
|
-
else { sub(/[ \t]+#.*$/, "", v); sub(/[ \t]+$/, "", v) }
|
|
197
|
-
print v
|
|
198
|
-
exit
|
|
199
|
-
}
|
|
200
|
-
' "$1"
|
|
201
|
-
}
|
|
174
|
+
# qmap_validate_document <file> <block> — structural validation only.
|
|
175
|
+
qmap_validate_document() { _qmap_read validate "$1" "$2"; }
|
|
202
176
|
|
|
203
|
-
#
|
|
204
|
-
|
|
205
|
-
|
|
206
|
-
|
|
207
|
-
|
|
208
|
-
|
|
209
|
-
|
|
210
|
-
|
|
211
|
-
s = substr($0, 4); q = index(s, "\"")
|
|
212
|
-
if (q == 0 || substr(s, 1, q - 1) != key) next
|
|
213
|
-
v = substr(s, q + 1)
|
|
214
|
-
sub(/^:[ \t]*/, "", v)
|
|
215
|
-
if (v ~ /^"/) { v = substr(v, 2); q2 = index(v, "\""); if (q2 > 0) v = substr(v, 1, q2 - 1) }
|
|
216
|
-
else { sub(/[ \t]+#.*$/, "", v); sub(/[ \t]+$/, "", v) }
|
|
217
|
-
if (v != "") print v
|
|
218
|
-
exit
|
|
219
|
-
}
|
|
220
|
-
' "$1"
|
|
221
|
-
}
|
|
177
|
+
# qmap_keys <file> <block> — quoted keys in document order.
|
|
178
|
+
qmap_keys() { _qmap_read keys "$1" "$2"; }
|
|
179
|
+
|
|
180
|
+
# qmap_field <file> <block> <key> <field> — decoded nested scalar.
|
|
181
|
+
qmap_field() { _qmap_read field "$1" "$2" "$3" "$4"; }
|
|
182
|
+
|
|
183
|
+
# qmap_value <file> <block> <key> — decoded flat scalar.
|
|
184
|
+
qmap_value() { _qmap_read value "$1" "$2" "$3"; }
|
|
222
185
|
|
|
223
186
|
# _qmap_stage <file> <awk-program> [awk args…] — run an editing pass, verify
|
|
224
187
|
# it produced output, commit.
|
|
@@ -245,7 +208,8 @@ qmap_set() {
|
|
|
245
208
|
# url/path values can carry a `"`; escape before it reaches the quoted
|
|
246
209
|
# scalar the writer emits.
|
|
247
210
|
value="$(_yq_esc "$value")"
|
|
248
|
-
_qmap_stage "$file" -v block="$block" -v key="$key" -v field="$field"
|
|
211
|
+
QMAP_EDIT_VALUE="$value" _qmap_stage "$file" -v block="$block" -v key="$key" -v field="$field" '
|
|
212
|
+
BEGIN { value = ENVIRON["QMAP_EDIT_VALUE"] }
|
|
249
213
|
function keyline() { return " \"" key "\":" }
|
|
250
214
|
function fieldline() { return " " field ": \"" value "\"" }
|
|
251
215
|
function flush_key() {
|
|
@@ -292,7 +256,8 @@ qmap_set_value() {
|
|
|
292
256
|
local file="$1" block="$2" key="$3" value="$4"
|
|
293
257
|
[ -f "$file" ] || die "no such file: $file"
|
|
294
258
|
value="$(_yq_esc "$value")"
|
|
295
|
-
_qmap_stage "$file" -v block="$block" -v key="$key"
|
|
259
|
+
QMAP_EDIT_VALUE="$value" _qmap_stage "$file" -v block="$block" -v key="$key" '
|
|
260
|
+
BEGIN { value = ENVIRON["QMAP_EDIT_VALUE"] }
|
|
296
261
|
function entry() { return " \"" key "\": \"" value "\"" }
|
|
297
262
|
{ sub(/\r$/, "") }
|
|
298
263
|
$0 ~ "^" block ":[ \t]*$" { blockseen = 1; inb = 1; print; next }
|
package/cli/lib/qmap.awk
ADDED
|
@@ -0,0 +1,191 @@
|
|
|
1
|
+
# Shared tokenizer for the CLI-owned quoted-key map and scalar document shape.
|
|
2
|
+
# Reader modes ignore unrelated YAML. Lock mode validates the whole document
|
|
3
|
+
# before publishing rows, so a bad later entry never yields a partial result.
|
|
4
|
+
BEGIN {
|
|
5
|
+
mode = ENVIRON["QMAP_MODE"]
|
|
6
|
+
block = ENVIRON["QMAP_BLOCK"]
|
|
7
|
+
wanted_key = ENVIRON["QMAP_KEY"]
|
|
8
|
+
wanted_field = ENVIRON["QMAP_FIELD"]
|
|
9
|
+
expected_version = ENVIRON["QMAP_EXPECTED_VERSION"]
|
|
10
|
+
source_file = ENVIRON["QMAP_FILE"]
|
|
11
|
+
strict = (mode == "lock" || mode == "validate")
|
|
12
|
+
sep = sprintf("%c", 31)
|
|
13
|
+
single_quote = sprintf("%c", 39)
|
|
14
|
+
}
|
|
15
|
+
|
|
16
|
+
function error_at(reason, line) {
|
|
17
|
+
if (problem == "") {
|
|
18
|
+
problem = reason
|
|
19
|
+
problem_line = line
|
|
20
|
+
}
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
# quoted() also parses keys. quote_end leaves the caller at the delimiter;
|
|
24
|
+
# decoded contains literal bytes, with the writer's two escapes decoded once.
|
|
25
|
+
function quoted(s, i, c, escaped) {
|
|
26
|
+
decoded = ""
|
|
27
|
+
quote_end = 0
|
|
28
|
+
for (i = 2; i <= length(s); i++) {
|
|
29
|
+
c = substr(s, i, 1)
|
|
30
|
+
if (c == "\"") {
|
|
31
|
+
quote_end = i
|
|
32
|
+
return 1
|
|
33
|
+
}
|
|
34
|
+
if (c == "\\") {
|
|
35
|
+
if (++i > length(s)) return 0
|
|
36
|
+
escaped = substr(s, i, 1)
|
|
37
|
+
if (escaped == "\\" || escaped == "\"") c = escaped
|
|
38
|
+
else {
|
|
39
|
+
if (strict) return 0
|
|
40
|
+
c = "\\" escaped
|
|
41
|
+
}
|
|
42
|
+
}
|
|
43
|
+
if (strict && c ~ /[[:cntrl:]]/) return 0
|
|
44
|
+
decoded = decoded c
|
|
45
|
+
}
|
|
46
|
+
return 0
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
function scalar(s, tail) {
|
|
50
|
+
decoded = ""
|
|
51
|
+
empty_scalar = 0
|
|
52
|
+
sub(/^[ \t]+/, "", s)
|
|
53
|
+
if (s == "" || s ~ /^#/) {
|
|
54
|
+
empty_scalar = 1
|
|
55
|
+
return 1
|
|
56
|
+
}
|
|
57
|
+
if (s ~ /^"/) {
|
|
58
|
+
if (!quoted(s)) return 0
|
|
59
|
+
tail = substr(s, quote_end + 1)
|
|
60
|
+
return !strict || tail ~ /^([ \t]*|[ \t]+#.*)$/
|
|
61
|
+
}
|
|
62
|
+
sub(/[ \t]+#.*$/, "", s)
|
|
63
|
+
sub(/[ \t]+$/, "", s)
|
|
64
|
+
if (strict) {
|
|
65
|
+
if (s ~ /[[:cntrl:]]/ || substr(s, 1, 1) == single_quote) return 0
|
|
66
|
+
if (s ~ /^[!&*\[\]{}>|%@`]/ || s ~ /^[?:-]([ \t]|$)/ || s ~ /"|:[ \t]/) return 0
|
|
67
|
+
if (s ~ /^(null|Null|NULL|~|true|True|TRUE|false|False|FALSE)$/) return 0
|
|
68
|
+
}
|
|
69
|
+
decoded = s
|
|
70
|
+
return 1
|
|
71
|
+
}
|
|
72
|
+
|
|
73
|
+
# Tokenization is shared by permissive reads and strict lock validation.
|
|
74
|
+
function token(line, text, colon, tail) {
|
|
75
|
+
kind = ""
|
|
76
|
+
key = ""
|
|
77
|
+
value = ""
|
|
78
|
+
empty = 0
|
|
79
|
+
if (line ~ /^[A-Za-z_]/) {
|
|
80
|
+
kind = "top"
|
|
81
|
+
text = line
|
|
82
|
+
} else if (line ~ /^ [A-Za-z_]/) {
|
|
83
|
+
kind = "field"
|
|
84
|
+
text = substr(line, 5)
|
|
85
|
+
} else if (line ~ /^ "/) {
|
|
86
|
+
kind = "package"
|
|
87
|
+
text = substr(line, 3)
|
|
88
|
+
if (!quoted(text)) return 0
|
|
89
|
+
key = decoded
|
|
90
|
+
tail = substr(text, quote_end + 1)
|
|
91
|
+
if (substr(tail, 1, 1) != ":") return 0
|
|
92
|
+
if (!scalar(substr(tail, 2))) return 0
|
|
93
|
+
value = decoded
|
|
94
|
+
empty = empty_scalar
|
|
95
|
+
return 1
|
|
96
|
+
} else return 0
|
|
97
|
+
colon = index(text, ":")
|
|
98
|
+
if (!colon) return 0
|
|
99
|
+
key = substr(text, 1, colon - 1)
|
|
100
|
+
if (strict && key !~ /^[A-Za-z_][A-Za-z0-9_]*$/) return 0
|
|
101
|
+
if (!scalar(substr(text, colon + 1))) return 0
|
|
102
|
+
value = decoded
|
|
103
|
+
empty = empty_scalar
|
|
104
|
+
return 1
|
|
105
|
+
}
|
|
106
|
+
|
|
107
|
+
{
|
|
108
|
+
sub(/\r$/, "")
|
|
109
|
+
if ($0 ~ /^[ \t]*(#.*)?$/) next
|
|
110
|
+
if (!token($0)) {
|
|
111
|
+
if (strict) {
|
|
112
|
+
context = ""
|
|
113
|
+
if (key ~ /^[A-Za-z_][A-Za-z0-9_]*$/) {
|
|
114
|
+
if (kind == "field" && package != "") context = "package " package ", field " key ": "
|
|
115
|
+
else if (kind == "top") context = "field " key ": "
|
|
116
|
+
}
|
|
117
|
+
error_at(context "malformed lock structure or unsupported scalar", NR)
|
|
118
|
+
}
|
|
119
|
+
if ($0 ~ /^[^ #\t]/) { in_block = 0; package = "" }
|
|
120
|
+
next
|
|
121
|
+
}
|
|
122
|
+
if (kind == "top") {
|
|
123
|
+
in_block = (key == block && empty)
|
|
124
|
+
package = ""
|
|
125
|
+
if (strict && top_seen[key]++) error_at("duplicate top-level field " key, NR)
|
|
126
|
+
if (!(key in top_value)) top_value[key] = value
|
|
127
|
+
if (mode == "top" && key == wanted_key && !found) { result = value; found = 1 }
|
|
128
|
+
if (strict) {
|
|
129
|
+
if (key == block) {
|
|
130
|
+
block_seen = 1
|
|
131
|
+
if (!empty) error_at(block " must be a block map", NR)
|
|
132
|
+
} else if (empty) error_at("expected scalar for " key, NR)
|
|
133
|
+
}
|
|
134
|
+
next
|
|
135
|
+
}
|
|
136
|
+
if (kind == "package" && in_block) {
|
|
137
|
+
package = key
|
|
138
|
+
row_count++
|
|
139
|
+
row_name[row_count] = key
|
|
140
|
+
row_line[row_count] = NR
|
|
141
|
+
if (strict) {
|
|
142
|
+
if (key == "") error_at("empty package key", NR)
|
|
143
|
+
if (package_seen[key]++) error_at("duplicate package " key, NR)
|
|
144
|
+
if (!empty) error_at("expected field map for " key, NR)
|
|
145
|
+
}
|
|
146
|
+
if (mode == "value" && key == wanted_key && !found) { result = value; found = 1 }
|
|
147
|
+
next
|
|
148
|
+
}
|
|
149
|
+
if (kind == "field" && in_block && package != "") {
|
|
150
|
+
identity = package SUBSEP key
|
|
151
|
+
if (strict && field_seen[identity]++) error_at("duplicate field " package "." key, NR)
|
|
152
|
+
if (!(identity in field_value)) field_value[identity] = value
|
|
153
|
+
if (strict && empty) error_at("expected scalar for " package "." key, NR)
|
|
154
|
+
if (mode == "field" && package == wanted_key && key == wanted_field && !found) {
|
|
155
|
+
result = value
|
|
156
|
+
found = 1
|
|
157
|
+
}
|
|
158
|
+
next
|
|
159
|
+
}
|
|
160
|
+
if (strict) error_at("malformed lock structure: expected top-level scalar or quoted package field", NR)
|
|
161
|
+
}
|
|
162
|
+
|
|
163
|
+
END {
|
|
164
|
+
# Read the format header before interpreting a future body's shape. Errors
|
|
165
|
+
# were retained during the single pass so header placement does not matter.
|
|
166
|
+
if (mode == "lock" && top_value["lockfile_version"] != expected_version) {
|
|
167
|
+
version = top_value["lockfile_version"]
|
|
168
|
+
print source_file " has unsupported or missing lockfile_version '" (version == "" ? "<missing>" : version) "' — expected " expected_version > "/dev/stderr"
|
|
169
|
+
exit 1
|
|
170
|
+
}
|
|
171
|
+
if (strict && problem != "") {
|
|
172
|
+
print source_file ":" problem_line ": " problem > "/dev/stderr"
|
|
173
|
+
exit 1
|
|
174
|
+
}
|
|
175
|
+
if (strict && !block_seen) {
|
|
176
|
+
print source_file ": missing " block " block" > "/dev/stderr"
|
|
177
|
+
exit 1
|
|
178
|
+
}
|
|
179
|
+
if (mode == "lock") print "V" sep top_value["lockfile_version"] sep top_value["engine_version"]
|
|
180
|
+
if (mode == "rows" || mode == "lock" || mode == "keys") {
|
|
181
|
+
for (i = 1; i <= row_count; i++) {
|
|
182
|
+
name = row_name[i]
|
|
183
|
+
if (mode == "keys") print name
|
|
184
|
+
else {
|
|
185
|
+
row = name sep field_value[name SUBSEP "requested"] sep field_value[name SUBSEP "url"] sep field_value[name SUBSEP "path"] sep field_value[name SUBSEP "resolved"] sep field_value[name SUBSEP "sha"]
|
|
186
|
+
if (mode == "lock") print "R" sep row sep row_line[i]
|
|
187
|
+
else print row
|
|
188
|
+
}
|
|
189
|
+
}
|
|
190
|
+
} else if (found) print result
|
|
191
|
+
}
|
package/cli/lib/registry.sh
CHANGED
|
@@ -122,6 +122,7 @@ fetch_package() {
|
|
|
122
122
|
local url="$1" ref="$2" subpath="$3" dest="$4" locked_sha="${5:-}" bundle_sha=""
|
|
123
123
|
assert_safe_source_url "$url"
|
|
124
124
|
assert_safe_ref "$ref"
|
|
125
|
+
assert_safe_package_path "$subpath"
|
|
125
126
|
# Bundle seed: the engine-content package at the CLI's own version copies
|
|
126
127
|
# from the npm bundle — no network, which keeps init, sync restoration and
|
|
127
128
|
# archived-project conversion offline. Keyed on the
|
|
@@ -186,9 +187,6 @@ fetch_package() {
|
|
|
186
187
|
fi
|
|
187
188
|
src="$tmp/clone"
|
|
188
189
|
if [ -n "$subpath" ]; then
|
|
189
|
-
case "$subpath" in
|
|
190
|
-
*..*|/*|*[\"\']*) rm -rf "$tmp"; die "unsafe path '$subpath'" ;;
|
|
191
|
-
esac
|
|
192
190
|
src="$tmp/clone/$subpath"
|
|
193
191
|
[ -d "$src" ] || { rm -rf "$tmp"; die "path '$subpath' not found in $url${ref:+@$ref}" ; }
|
|
194
192
|
fi
|
package/engine/ENGINE_SHA
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
|
|
1
|
+
fe187a9bee5923603cec51798bd1ea5c2860593e
|
package/engine/VERSION
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
0.11.
|
|
1
|
+
0.11.9
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@ainova-systems/intelligence",
|
|
3
|
-
"version": "0.11.
|
|
3
|
+
"version": "0.11.9",
|
|
4
4
|
"description": "Build, version and distribute AI agent intelligence across your organization — one CLI, versioned Intelligence Packages, and a sync engine for Claude Code, Cursor, Copilot, Codex, Pi and OpenCode.",
|
|
5
5
|
"bin": {
|
|
6
6
|
"intelligence": "bin/intelligence.js"
|