@guanlin4924/devflow-pi 0.1.75 → 0.1.76

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/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@guanlin4924/devflow-pi",
3
- "version": "0.1.75",
3
+ "version": "0.1.76",
4
4
  "type": "module",
5
5
  "description": "Devflow 专项研发 Pi,基于固定版本 OMP Runtime 组装研发状态机、代码智能、设计、实现与质量门禁。",
6
6
  "license": "UNLICENSED",
@@ -1318,7 +1318,7 @@ export default function devflowCore(pi: ExtensionAPI) {
1318
1318
  value = await store.registerWorkspace({ rebindWorkspaceId: params.workspaceId });
1319
1319
  } else if (params.action === "cancel") {
1320
1320
  if (!params.runId) throw new Error("runId is required for cancel");
1321
- value = { runId: params.runId, cancelled: store.cancelRun(params.runId) };
1321
+ value = { runId: params.runId, cancelled: store.cancelRunByOperator(params.runId) };
1322
1322
  } else if (params.action === "recover") {
1323
1323
  if (!params.runId) throw new Error("runId is required for recover");
1324
1324
  value = { runId: params.runId, ...store.recoverRun(params.runId) };
@@ -61,6 +61,8 @@ def validate(database: Path, project_id: str, workspace_id: str | None = None, r
61
61
  for run in runs[:1] if not run_id else runs:
62
62
  if run["status"] not in TERMINAL_STATUSES | {"preparing", "collecting", "validating"}:
63
63
  errors.append(f"invalid run status: {run['status']}")
64
+ if run["status"] != "finished":
65
+ errors.append(f"initialization run is not finished: {run['status']}")
64
66
  results = connection.execute(
65
67
  "SELECT repository,kind,status FROM collection_results WHERE run_id=?", (run["id"],)
66
68
  ).fetchall()
@@ -19,11 +19,14 @@ def build_semantic_facts(root: Path, files: Iterable[dict], symbols: Iterable[di
19
19
  entities: List[dict] = []
20
20
  flows: List[dict] = []
21
21
  bindings: List[dict] = []
22
+ records = list(files)
22
23
  symbol_by_path = {}
23
24
  for symbol in symbols:
24
25
  symbol_by_path.setdefault(symbol.get("path"), []).append(symbol)
25
26
 
26
- for file_record in files:
27
+ for file_number, file_record in enumerate(records, 1):
28
+ if file_number == 1 or file_number % 250 == 0 or file_number == len(records):
29
+ print(f"[code-index][semantic] processed {file_number}/{len(records)} files", flush=True)
27
30
  path = file_record["path"]
28
31
  source = _read(root, path)
29
32
  if source is None:
@@ -45,10 +48,11 @@ def build_semantic_facts(root: Path, files: Iterable[dict], symbols: Iterable[di
45
48
  if symbol.get("kind") in {"route", "component", "class", "function", "method"}:
46
49
  bindings.append(_binding(feature_id, symbol["id"], "static_inferred", "heuristic", path, symbol.get("line_start", 1)))
47
50
 
51
+ state_ids = set()
48
52
  for line_no, line in enumerate(source.splitlines(), 1):
49
53
  _extract_api(line, path, file_record, line_no, entities, flows)
50
54
  _extract_db(line, path, file_record, line_no, entities, flows)
51
- _extract_ui(line, path, file_record, line_no, entities, flows)
55
+ _extract_ui(line, path, file_record, line_no, entities, flows, state_ids)
52
56
 
53
57
  # Existing structural relations remain the source of method call edges;
54
58
  # expose them in the semantic graph without changing their meaning.
@@ -110,7 +114,8 @@ def _extract_db(line, path, record, line_no, entities, flows):
110
114
  flows.append(_flow(table_id, column_id, f"{operation}_column", path, line_no, "heuristic", "sql_column_regex"))
111
115
 
112
116
 
113
- def _extract_ui(line, path, record, line_no, entities, flows):
117
+ def _extract_ui(line, path, record, line_no, entities, flows, state_ids=None):
118
+ state_ids = state_ids if state_ids is not None else set()
114
119
  component = re.search(r"\b(?:function|class)\s+([A-Z][A-Za-z0-9_]*)|<([A-Z][A-Za-z0-9_.]*)\b", line)
115
120
  state = re.search(r"\b(?:useState|ref|reactive|computed|createSignal)\s*\(.*?\)|\b(?:const|let|var)\s+([A-Za-z_][\w]*(?:State|Store|Status|Loading|Error))\s*=", line)
116
121
  condition = re.search(r"\b(?:v-if|v-show)\s*=\s*['\"]([^'\"]+)['\"]|(?:\{\s*)?([^{}\n]+?)\s*&&\s*<", line)
@@ -125,16 +130,16 @@ def _extract_ui(line, path, record, line_no, entities, flows):
125
130
  name = state.group(1) or "anonymous_state"
126
131
  state_id = f"ui-state:{stable_key(path)}:{stable_key(name)}"
127
132
  entities.append(_entity(state_id, "ui_state", name, record, line_no, line_no, "heuristic", "state_pattern"))
133
+ state_ids.add(state_id)
128
134
  flows.append(_flow(component_id, state_id, "state_reads", path, line_no, "heuristic", "state_pattern"))
129
135
  if condition:
130
136
  expression = condition.group(1) or condition.group(2)
131
137
  cond_id = f"render-condition:{stable_key(path)}:{short_hash(expression)}"
132
138
  entities.append(_entity(cond_id, "render_condition", expression, record, line_no, line_no, "heuristic", "template_condition"))
133
139
  flows.append(_flow(component_id, cond_id, "renders_when", path, line_no, "heuristic", "template_condition"))
134
- known_ids = {item["id"] for item in entities if item.get("entity_type") == "ui_state"}
135
140
  for name in re.findall(r"\b[A-Za-z_][A-Za-z0-9_]*\b", expression):
136
141
  state_id = f"ui-state:{stable_key(path)}:{stable_key(name)}"
137
- if state_id in known_ids:
142
+ if state_id in state_ids:
138
143
  flows.append(_flow(cond_id, state_id, "depends_on", path, line_no, "heuristic", "condition_identifier"))
139
144
 
140
145
 
package/src/constants.ts CHANGED
@@ -1,5 +1,5 @@
1
1
  export const PRODUCT_NAME = "Devflow Pi";
2
- export const PRODUCT_VERSION = "0.1.75";
2
+ export const PRODUCT_VERSION = "0.1.76";
3
3
  export const PROJECT_SCHEMA_VERSION = 1;
4
4
  export const OMP_RUNTIME_VERSION = "17.2.7";
5
5
 
@@ -455,6 +455,17 @@ export class InitializationStore {
455
455
  return this.db.query("UPDATE initialization_runs SET status='cancelled',updated_at=? WHERE id=? AND lease_token=? AND lease_expires_at>? AND status IN ('preparing','collecting','validating')").run(timestamp, runId, leaseToken, timestamp).changes === 1;
456
456
  }
457
457
 
458
+ /** Operator cancellation invalidates the worker lease so a detached worker cannot publish later. */
459
+ cancelRunByOperator(runId: string, reason = "cancelled by operator"): boolean {
460
+ const replacementLease = randomUUID();
461
+ const result = this.db.query(`UPDATE initialization_runs
462
+ SET status='cancelled', lease_token=?, error=?, updated_at=?
463
+ WHERE id=? AND status IN ('preparing','collecting','validating')`).run(
464
+ replacementLease, reason.slice(0, 4000), now(), runId,
465
+ );
466
+ return result.changes === 1;
467
+ }
468
+
458
469
  failRun(runId: string, leaseToken: string, error: string): boolean {
459
470
  this.assertWritableRun(runId, leaseToken);
460
471
  const timestamp = now();