@construct-space/cli 1.1.12 → 1.3.1

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/dist/index.js CHANGED
@@ -5,43 +5,25 @@ var __getProtoOf = Object.getPrototypeOf;
5
5
  var __defProp = Object.defineProperty;
6
6
  var __getOwnPropNames = Object.getOwnPropertyNames;
7
7
  var __hasOwnProp = Object.prototype.hasOwnProperty;
8
- function __accessProp(key) {
9
- return this[key];
10
- }
11
- var __toESMCache_node;
12
- var __toESMCache_esm;
13
8
  var __toESM = (mod, isNodeMode, target) => {
14
- var canCache = mod != null && typeof mod === "object";
15
- if (canCache) {
16
- var cache = isNodeMode ? __toESMCache_node ??= new WeakMap : __toESMCache_esm ??= new WeakMap;
17
- var cached = cache.get(mod);
18
- if (cached)
19
- return cached;
20
- }
21
9
  target = mod != null ? __create(__getProtoOf(mod)) : {};
22
10
  const to = isNodeMode || !mod || !mod.__esModule ? __defProp(target, "default", { value: mod, enumerable: true }) : target;
23
11
  for (let key of __getOwnPropNames(mod))
24
12
  if (!__hasOwnProp.call(to, key))
25
13
  __defProp(to, key, {
26
- get: __accessProp.bind(mod, key),
14
+ get: () => mod[key],
27
15
  enumerable: true
28
16
  });
29
- if (canCache)
30
- cache.set(mod, to);
31
17
  return to;
32
18
  };
33
19
  var __commonJS = (cb, mod) => () => (mod || cb((mod = { exports: {} }).exports, mod), mod.exports);
34
- var __returnValue = (v) => v;
35
- function __exportSetter(name, newValue) {
36
- this[name] = __returnValue.bind(null, newValue);
37
- }
38
20
  var __export = (target, all) => {
39
21
  for (var name in all)
40
22
  __defProp(target, name, {
41
23
  get: all[name],
42
24
  enumerable: true,
43
25
  configurable: true,
44
- set: __exportSetter.bind(all, name)
26
+ set: (newValue) => all[name] = () => newValue
45
27
  });
46
28
  };
47
29
  var __require = import.meta.require;
@@ -2807,6 +2789,7 @@ Object.defineProperties(createChalk.prototype, styles2);
2807
2789
  var chalk = createChalk();
2808
2790
  var chalkStderr = createChalk({ level: stderrColor ? stderrColor.level : 0 });
2809
2791
  var source_default = chalk;
2792
+
2810
2793
  // node_modules/@inquirer/core/dist/lib/key.js
2811
2794
  var isUpKey = (key, keybindings = []) => key.name === "up" || keybindings.includes("vim") && key.name === "k" || keybindings.includes("emacs") && key.ctrl && key.name === "p";
2812
2795
  var isDownKey = (key, keybindings = []) => key.name === "down" || keybindings.includes("vim") && key.name === "j" || keybindings.includes("emacs") && key.ctrl && key.name === "n";
@@ -2951,7 +2934,7 @@ var effectScheduler = {
2951
2934
  // node_modules/@inquirer/core/dist/lib/use-state.js
2952
2935
  function useState(defaultValue) {
2953
2936
  return withPointer((pointer) => {
2954
- const setState = AsyncResource2.bind(function setState2(newValue) {
2937
+ const setState = AsyncResource2.bind(function setState(newValue) {
2955
2938
  if (pointer.get() !== newValue) {
2956
2939
  pointer.set(newValue);
2957
2940
  handleChange();
@@ -4524,22 +4507,6 @@ var dist_default6 = createPrompt((config, done) => {
4524
4507
  });
4525
4508
  // src/lib/utils.ts
4526
4509
  import { execSync } from "child_process";
4527
- import { platform } from "os";
4528
- function openBrowser(url) {
4529
- try {
4530
- switch (platform()) {
4531
- case "darwin":
4532
- execSync(`open "${url}"`);
4533
- break;
4534
- case "linux":
4535
- execSync(`xdg-open "${url}"`);
4536
- break;
4537
- case "win32":
4538
- execSync(`rundll32 url.dll,FileProtocolHandler "${url}"`);
4539
- break;
4540
- }
4541
- } catch {}
4542
- }
4543
4510
  function git(dir, ...args) {
4544
4511
  const quoted = args.map((a) => a.includes(" ") || a.includes(":") ? `"${a}"` : a);
4545
4512
  return execSync(`git ${quoted.join(" ")}`, { cwd: dir, encoding: "utf-8" }).trim();
@@ -4613,616 +4580,18 @@ function runHook(hooks, hookName, root) {
4613
4580
  execSync2(cmd, { cwd: root, stdio: "inherit" });
4614
4581
  }
4615
4582
 
4616
- // src/lib/embedded-templates.ts
4617
- var EMBEDDED_TEMPLATES = {
4618
- "package.json.tmpl": `{
4619
- "name": "@construct-spaces/{{.Name}}",
4620
- "version": "0.1.0",
4621
- "private": true,
4622
- "type": "module",
4623
- "scripts": {
4624
- "generate-entry": "construct build --entry-only",
4625
- "build": "construct build",
4626
- "dev": "construct dev",
4627
- "check": "construct check",
4628
- "validate": "construct validate"
4629
- },
4630
- "peerDependencies": {
4631
- "vue": "^3.5.0",
4632
- "pinia": "^3.0.0",
4633
- "@vueuse/core": "^14.0.0"
4634
- },
4635
- "devDependencies": {
4636
- "@construct-space/cli": "latest",
4637
- "@construct-space/sdk": "latest",
4638
- "@vitejs/plugin-vue": "^5.2.3",
4639
- "lucide-vue-next": "^1.0.0",
4640
- "typescript": "^5.9.3",
4641
- "vite": "^6.3.5",
4642
- "vue-tsc": "^3.2.5"
4643
- }
4644
- }
4645
- `,
4646
- "config.md.tmpl": `---
4647
- id: {{.ID}}
4648
- name: {{.DisplayName}}
4649
- category: space
4650
- description: AI agent for the {{.DisplayName}} space
4651
- maxIterations: 15
4652
- tools: []
4653
- canInvokeAgents: []
4654
- ---
4655
-
4656
- You are Construct's {{.DisplayName}} agent. You help users work within the {{.DisplayName}} space.
4657
-
4658
- ## Context
4659
-
4660
- Use space_list_actions to discover available actions for this space.
4661
- Use space_run_action to execute actions.
4662
- Do NOT call get_project_context \u2014 you work with space content, not project files.
4663
-
4664
- ## Behavior
4665
-
4666
- - Start by listing available actions to understand what you can do
4667
- - Be concise and action-oriented
4668
- - Focus on tasks relevant to this space
4669
- `,
4670
- "full/skill-data.md.tmpl": `---
4671
- id: {{.ID}}-data
4672
- name: {{.DisplayName}} Data Management
4673
- description: Skill for managing {{.DisplayName}} data and content
4674
- trigger: {{.ID}} data|content|manage
4675
- category: space
4676
- tools: [read_file, list_dir, glob, grep, write_file]
4677
- ---
4678
-
4679
- # {{.DisplayName}} Data Management Skill
4680
-
4681
- This skill handles data operations for the {{.DisplayName}} space.
4682
-
4683
- ## Instructions
4684
- - Help users create, read, update, and delete content
4685
- - Validate data before writing
4686
- - Use the space actions API when available
4687
- - Prefer batch operations for bulk changes
4688
- `,
4689
- "full/entry.ts.tmpl": `// Space entry \u2014 exports pages, widgets, and actions for the host loader.
4690
- // \`construct dev\` regenerates this from space.manifest.json on changes.
4691
- import IndexPage from './pages/index.vue'
4692
- import SettingsPage from './pages/settings.vue'
4693
- import { actions } from './actions'
4694
-
4695
- const spaceExport = {
4696
- pages: {
4697
- '': IndexPage,
4698
- 'settings': SettingsPage,
4699
- },
4700
- widgets: {},
4701
- actions,
4702
- }
4703
-
4704
- export default spaceExport
4705
- `,
4706
- "full/skill-ui.md.tmpl": `---
4707
- id: {{.ID}}-ui
4708
- name: {{.DisplayName}} UI Customization
4709
- description: Skill for customizing the {{.DisplayName}} user interface
4710
- trigger: {{.ID}} ui|layout|design|style
4711
- category: space
4712
- tools: [read_file, write_file, glob]
4713
- ---
4714
-
4715
- # {{.DisplayName}} UI Customization Skill
4716
-
4717
- This skill helps customize the {{.DisplayName}} space interface.
4718
-
4719
- ## Instructions
4720
- - Help users adjust layout and styling
4721
- - Use Construct CSS variables (--app-foreground, --app-background, --app-accent, etc.)
4722
- - Follow the host design system conventions
4723
- - Suggest accessible color combinations
4724
- `,
4725
- "full/space.manifest.json.tmpl": `{
4726
- "id": "{{.ID}}",
4727
- "name": "{{.DisplayName}}",
4728
- "version": "0.1.0",
4729
- "description": "{{.DisplayName}} space for Construct",
4730
- "author": {
4731
- "name": "Your Name"
4732
- },
4733
- "icon": "i-lucide-box",
4734
- "scope": "both",
4735
- "minConstructVersion": "0.7.0",
4736
- "navigation": {
4737
- "label": "{{.DisplayName}}",
4738
- "icon": "i-lucide-box",
4739
- "to": "{{.ID}}",
4740
- "order": 100
4741
- },
4742
- "pages": [
4743
- {
4744
- "path": "",
4745
- "label": "Home",
4746
- "icon": "i-lucide-home",
4747
- "default": true
4748
- },
4749
- {
4750
- "path": "settings",
4751
- "label": "Settings",
4752
- "icon": "i-lucide-settings"
4753
- }
4754
- ],
4755
- "toolbar": [
4756
- {
4757
- "id": "{{.ID}}-new",
4758
- "icon": "i-lucide-plus",
4759
- "label": "New {{.DisplayName}}",
4760
- "action": "create{{.DisplayNameNoSpace}}"
4761
- }
4762
- ],
4763
- "keywords": ["{{.ID}}"],
4764
- "agent": "agent/config.md",
4765
- "skills": [
4766
- "agent/skills/default.md",
4767
- "agent/skills/data.md",
4768
- "agent/skills/ui.md"
4769
- ],
4770
- "actions": "src/actions.ts",
4771
- "widgets": [
4772
- {
4773
- "id": "summary",
4774
- "name": "{{.DisplayName}} Summary",
4775
- "description": "Quick summary widget for {{.DisplayName}}",
4776
- "icon": "i-lucide-box",
4777
- "defaultSize": "4x1",
4778
- "sizes": {
4779
- "2x1": "widgets/summary/2x1.vue",
4780
- "4x1": "widgets/summary/4x1.vue"
4781
- }
4782
- }
4783
- ]
4784
- }
4785
- `,
4786
- "full/settings.vue.tmpl": `<script setup lang="ts">
4787
- /**
4788
- * {{.DisplayName}} \u2014 Settings page
4789
- */
4790
- import { ref } from 'vue'
4791
-
4792
- const autoSave = ref(true)
4793
- const theme = ref<'system' | 'light' | 'dark'>('system')
4794
- </script>
4795
-
4796
- <template>
4797
- <div class="h-full overflow-auto p-6">
4798
- <h1 class="text-xl font-bold text-[var(--app-foreground)] mb-6">Settings</h1>
4799
-
4800
- <div class="space-y-4 max-w-md">
4801
- <label class="flex items-center justify-between">
4802
- <span class="text-sm text-[var(--app-foreground)]">Auto-save</span>
4803
- <input v-model="autoSave" type="checkbox" class="rounded" />
4804
- </label>
4805
-
4806
- <label class="block">
4807
- <span class="text-sm text-[var(--app-foreground)] block mb-1">Theme</span>
4808
- <select v-model="theme" class="w-full rounded border border-[var(--app-border)] bg-[var(--app-background)] px-3 py-1.5 text-sm">
4809
- <option value="system">System</option>
4810
- <option value="light">Light</option>
4811
- <option value="dark">Dark</option>
4812
- </select>
4813
- </label>
4814
- </div>
4815
- </div>
4816
- </template>
4817
- `,
4818
- "vite.config.ts.tmpl": `import { defineConfig } from 'vite'
4819
- import vue from '@vitejs/plugin-vue'
4820
- import { resolve } from 'path'
4821
-
4822
- /**
4823
- * Host-provided externals \u2014 these are supplied by Construct at runtime
4824
- * via window.__CONSTRUCT__. Do NOT bundle them; they must stay external.
4825
- *
4826
- * Full list lives in: construct-app/frontend/lib/spaceHost.ts
4827
- */
4828
- const hostExternals = [
4829
- 'vue',
4830
- 'vue-router',
4831
- 'pinia',
4832
- '@vueuse/core',
4833
- '@vueuse/integrations',
4834
- '@tauri-apps/api',
4835
- '@tauri-apps/api/core',
4836
- '@tauri-apps/api/path',
4837
- '@tauri-apps/api/event',
4838
- '@tauri-apps/api/webview',
4839
- '@tauri-apps/plugin-fs',
4840
- '@tauri-apps/plugin-shell',
4841
- '@tauri-apps/plugin-dialog',
4842
- '@tauri-apps/plugin-process',
4843
- 'lucide-vue-next',
4844
- 'date-fns',
4845
- 'dexie',
4846
- 'zod',
4847
- '@construct-space/ui',
4848
- '@construct/sdk',
4849
- '@construct-space/sdk',
4850
- ]
4851
-
4852
- function makeGlobals(externals: string[]): Record<string, string> {
4853
- const globals: Record<string, string> = {}
4854
- for (const ext of externals) {
4855
- // Simple ids use dot access, scoped/slashed ids use bracket access
4856
- if (/^[a-z]+$/.test(ext)) {
4857
- globals[ext] = \`window.__CONSTRUCT__.\${ext}\`
4858
- } else {
4859
- globals[ext] = \`window.__CONSTRUCT__["\${ext}"]\`
4860
- }
4861
- }
4862
- return globals
4863
- }
4864
-
4865
- export default defineConfig({
4866
- plugins: [vue()],
4867
- build: {
4868
- lib: {
4869
- entry: resolve(__dirname, 'src/entry.ts'),
4870
- name: '__CONSTRUCT_SPACE_{{.IDUpper}}',
4871
- fileName: 'space-{{.ID}}',
4872
- formats: ['iife'],
4873
- },
4874
- rollupOptions: {
4875
- external: hostExternals,
4876
- output: {
4877
- globals: makeGlobals(hostExternals),
4878
- },
4879
- },
4880
- },
4881
- resolve: {
4882
- alias: {
4883
- '~': resolve(__dirname),
4884
- '@': resolve(__dirname, 'src'),
4885
- },
4886
- },
4887
- })
4888
- `,
4889
- "index.vue.tmpl": `<script setup lang="ts">
4890
- /**
4891
- * {{.DisplayName}} \u2014 Home page
4892
- *
4893
- * Host-provided packages (vue, pinia, @vueuse/core, @construct/sdk, etc.)
4894
- * are available as imports \u2014 they resolve to the host at runtime.
4895
- */
4896
- import { ref } from 'vue'
4897
-
4898
- const greeting = ref('Your space is ready. Start building!')
4899
- </script>
4900
-
4901
- <template>
4902
- <div class="h-full flex items-center justify-center">
4903
- <div class="text-center">
4904
- <h1 class="text-2xl font-bold text-[var(--app-foreground)] mb-2">{{.DisplayName}}</h1>
4905
- <p class="text-sm text-[var(--app-muted)]">{{ greeting }}</p>
4906
- </div>
4907
- </div>
4908
- </template>
4909
- `,
4910
- "entry.ts.tmpl": `// Space entry \u2014 exports pages, widgets, and actions for the host loader.
4911
- // \`construct dev\` regenerates this from space.manifest.json on changes.
4912
- import IndexPage from './pages/index.vue'
4913
- import { actions } from './actions'
4914
-
4915
- const spaceExport = {
4916
- pages: {
4917
- '': IndexPage,
4918
- },
4919
- widgets: {},
4920
- actions,
4921
- }
4922
-
4923
- export default spaceExport
4924
- `,
4925
- "space.manifest.json.tmpl": `{
4926
- "id": "{{.ID}}",
4927
- "name": "{{.DisplayName}}",
4928
- "version": "0.1.0",
4929
- "description": "{{.DisplayName}} space for Construct",
4930
- "author": {
4931
- "name": "Your Name"
4932
- },
4933
- "icon": "i-lucide-box",
4934
- "scope": "both",
4935
- "minConstructVersion": "0.7.0",
4936
- "navigation": {
4937
- "label": "{{.DisplayName}}",
4938
- "icon": "i-lucide-box",
4939
- "to": "{{.ID}}",
4940
- "order": 100
4941
- },
4942
- "pages": [
4943
- {
4944
- "path": "",
4945
- "label": "Home",
4946
- "icon": "i-lucide-home",
4947
- "default": true
4948
- }
4949
- ],
4950
- "toolbar": [
4951
- {
4952
- "id": "{{.ID}}-new",
4953
- "icon": "i-lucide-plus",
4954
- "label": "New {{.DisplayName}}",
4955
- "action": "create{{.DisplayNameNoSpace}}"
4956
- }
4957
- ],
4958
- "keywords": ["{{.ID}}"],
4959
- "agent": "agent/config.md",
4960
- "skills": ["agent/skills/default.md"],
4961
- "actions": {},
4962
- "widgets": [
4963
- {
4964
- "id": "summary",
4965
- "name": "{{.DisplayName}} Summary",
4966
- "description": "Quick summary widget for {{.DisplayName}}",
4967
- "icon": "i-lucide-box",
4968
- "defaultSize": "4x1",
4969
- "sizes": {
4970
- "2x1": "widgets/summary/2x1.vue",
4971
- "4x1": "widgets/summary/4x1.vue"
4972
- }
4973
- }
4974
- ]
4975
- }
4976
- `,
4977
- "skill.md.tmpl": `---
4978
- id: {{.ID}}-default
4979
- name: {{.DisplayName}} Basics
4980
- description: Default skill for {{.DisplayName}} space
4981
- trigger: {{.ID}}|help
4982
- category: space
4983
- tools: [read_file, list_dir, glob, grep]
4984
- ---
4985
-
4986
- # {{.DisplayName}} Space Skill
4987
-
4988
- This skill provides default behavior for the {{.DisplayName}} space.
4989
-
4990
- ## Instructions
4991
- - Assist with {{.DisplayName}}-related tasks
4992
- - Follow project conventions
4993
- - Read relevant files before making suggestions
4994
- `,
4995
- "gitignore.tmpl": `node_modules/
4996
- dist/
4997
- src/entry.ts
4998
- *.local
4999
- .DS_Store
5000
- `,
5001
- "actions.ts.tmpl": `/**
5002
- * Space Actions \u2014 exposed to the AI agent via space_run_action
5003
- *
5004
- * Define actions here and they'll be automatically available as agent tools.
5005
- * The agent calls: space_run_action(action: "action_id", payload: {...})
5006
- */
5007
-
5008
- // --- Graph SDK (data layer) ---
5009
- // Uncomment after running \`construct graph init\` and defining models:
5010
- //
5011
- // import { useGraph } from '@construct-space/graph'
5012
- // const graph = useGraph()
5013
- //
5014
- // Example action using Graph:
5015
- // fetchItems: {
5016
- // description: 'Fetch items from the graph',
5017
- // params: {
5018
- // limit: { type: 'number', description: 'Max items to return', required: false },
5019
- // },
5020
- // run: async (p: any) => {
5021
- // const items = await graph.query('Item').limit(p.limit ?? 10).exec()
5022
- // return { items }
5023
- // },
5024
- // },
5025
-
5026
- export const actions = {
5027
- // Example action:
5028
- // hello: {
5029
- // description: 'Say hello',
5030
- // params: {
5031
- // name: { type: 'string', description: 'Name to greet', required: true },
5032
- // },
5033
- // run: (p: any) => ({ message: \`Hello \${p.name}!\` }),
5034
- // },
5035
- }
5036
- `,
5037
- "readme.md.tmpl": `# {{.DisplayName}}
5038
-
5039
- A Construct space.
5040
-
5041
- ## Development
5042
-
5043
- \`\`\`bash
5044
- bun install
5045
- construct space dev
5046
- \`\`\`
5047
-
5048
- ## Build
5049
-
5050
- \`\`\`bash
5051
- construct space build
5052
- \`\`\`
5053
-
5054
- Output goes to \`dist/\`.
5055
- `,
5056
- "safety.json.tmpl": `{
5057
- "hooks": [
5058
- {
5059
- "id": "{{.ID}}-no-rm-rf",
5060
- "type": "pre_tool",
5061
- "tools": ["bash"],
5062
- "command": "if echo \\"$TOOL_INPUT\\" | grep -qE 'rm\\\\s+-rf\\\\s+/'; then\\n echo '{\\"block\\":true,\\"message\\":\\"Blocked: rm -rf with absolute root path\\"}'\\nfi",
5063
- "source": "space:{{.ID}}"
5064
- }
5065
- ]
5066
- }
5067
- `,
5068
- "e2e/playwright.config.ts.tmpl": `import { defineConfig } from '@playwright/test'
5069
-
5070
- export default defineConfig({
5071
- testDir: './e2e',
5072
- timeout: 30_000,
5073
- retries: 0,
5074
- use: {
5075
- baseURL: 'http://localhost:5173',
5076
- trace: 'on-first-retry',
5077
- },
5078
- webServer: {
5079
- command: 'construct dev',
5080
- port: 5173,
5081
- reuseExistingServer: true,
5082
- },
5083
- })
5084
- `,
5085
- "e2e/space.spec.ts.tmpl": `import { test, expect } from '@playwright/test'
5086
-
5087
- test.describe('{{.DisplayName}} space', () => {
5088
- test('renders the default page', async ({ page }) => {
5089
- await page.goto('/')
5090
- await expect(page.locator('[data-testid="space-root"]')).toBeVisible()
5091
- })
5092
-
5093
- test('displays the space title', async ({ page }) => {
5094
- await page.goto('/')
5095
- await expect(page.locator('text={{.DisplayName}}')).toBeVisible()
5096
- })
5097
- })
5098
- `,
5099
- "build.yml.tmpl": `name: Build Space
5100
-
5101
- on:
5102
- push:
5103
- tags:
5104
- - 'v*'
5105
- pull_request:
5106
- branches: [main]
5107
-
5108
- jobs:
5109
- build:
5110
- runs-on: ubuntu-latest
5111
- steps:
5112
- - uses: actions/checkout@v4
5113
- - uses: oven-sh/setup-bun@v2
5114
- with:
5115
- bun-version: latest
5116
- - run: bun install
5117
- - run: bun run build
5118
- - name: Upload artifacts
5119
- if: startsWith(github.ref, 'refs/tags/v')
5120
- uses: actions/upload-artifact@v4
5121
- with:
5122
- name: space-dist
5123
- path: dist/
5124
-
5125
- release:
5126
- needs: build
5127
- if: startsWith(github.ref, 'refs/tags/v')
5128
- runs-on: ubuntu-latest
5129
- permissions:
5130
- contents: write
5131
- steps:
5132
- - uses: actions/download-artifact@v4
5133
- with:
5134
- name: space-dist
5135
- path: dist/
5136
- - run: tar -czf space-dist.tar.gz -C dist .
5137
- - uses: softprops/action-gh-release@v2
5138
- with:
5139
- files: |
5140
- space-dist.tar.gz
5141
- dist/manifest.json
5142
- generate_release_notes: true
5143
- `,
5144
- "widgets/2x1.vue.tmpl": `<script setup lang="ts">
5145
- /**
5146
- * {{.DisplayName}} Summary Widget \u2014 2\xD71 compact view
5147
- */
5148
- </script>
5149
-
5150
- <template>
5151
- <div class="h-full flex items-center gap-3 px-3">
5152
- <div class="size-8 rounded-lg bg-[var(--app-accent)]/10 flex items-center justify-center">
5153
- <Icon name="i-lucide-box" class="size-4 text-[var(--app-accent)]" />
5154
- </div>
5155
- <div class="min-w-0">
5156
- <p class="text-sm font-medium text-[var(--app-foreground)] truncate">{{.DisplayName}}</p>
5157
- <p class="text-xs text-[var(--app-muted)]">Ready</p>
5158
- </div>
5159
- </div>
5160
- </template>
5161
- `,
5162
- "widgets/4x1.vue.tmpl": `<script setup lang="ts">
5163
- /**
5164
- * {{.DisplayName}} Summary Widget \u2014 4\xD71 wide view
5165
- */
5166
- </script>
5167
-
5168
- <template>
5169
- <div class="h-full flex items-center justify-between px-4">
5170
- <div class="flex items-center gap-3">
5171
- <div class="size-8 rounded-lg bg-[var(--app-accent)]/10 flex items-center justify-center">
5172
- <Icon name="i-lucide-box" class="size-4 text-[var(--app-accent)]" />
5173
- </div>
5174
- <div>
5175
- <p class="text-sm font-medium text-[var(--app-foreground)]">{{.DisplayName}}</p>
5176
- <p class="text-xs text-[var(--app-muted)]">Your space is ready</p>
5177
- </div>
5178
- </div>
5179
- <div class="text-right">
5180
- <p class="text-lg font-bold text-[var(--app-foreground)]">0</p>
5181
- <p class="text-[10px] text-[var(--app-muted)] uppercase">Items</p>
5182
- </div>
5183
- </div>
5184
- </template>
5185
- `,
5186
- "tsconfig.json.tmpl": `{
5187
- "compilerOptions": {
5188
- "target": "ESNext",
5189
- "module": "ESNext",
5190
- "moduleResolution": "bundler",
5191
- "strict": true,
5192
- "jsx": "preserve",
5193
- "noEmit": true,
5194
- "esModuleInterop": true,
5195
- "skipLibCheck": true,
5196
- "paths": {
5197
- "@/*": ["./src/*"],
5198
- "@construct/sdk": ["./node_modules/@construct-space/sdk/src/index.ts"]
5199
- }
5200
- },
5201
- "include": [
5202
- "src/**/*.ts",
5203
- "src/**/*.vue",
5204
- "widgets/**/*.vue"
5205
- ]
5206
- }
5207
- `
5208
- };
5209
-
5210
4583
  // src/commands/scaffold.ts
5211
4584
  var nameRegex = /^[a-z][a-z0-9-]*$/;
5212
4585
  function render(template, data) {
5213
4586
  return template.replace(/\{\{\.Name\}\}/g, data.name).replace(/\{\{\.ID\}\}/g, data.id).replace(/\{\{\.IDUpper\}\}/g, data.idUpper).replace(/\{\{\.DisplayName\}\}/g, data.displayName).replace(/\{\{\.DisplayNameNoSpace\}\}/g, data.displayNameNoSpace);
5214
4587
  }
5215
4588
  function writeTemplate(templateDir, tmplName, outPath, data) {
5216
- let content;
5217
4589
  const tmplPath = join2(templateDir, tmplName);
5218
- if (existsSync2(tmplPath)) {
5219
- content = readFileSync(tmplPath, "utf-8");
5220
- } else if (EMBEDDED_TEMPLATES[tmplName] !== undefined) {
5221
- content = EMBEDDED_TEMPLATES[tmplName];
5222
- } else {
4590
+ if (!existsSync2(tmplPath)) {
5223
4591
  console.warn(source_default.yellow(`Template not found: ${tmplName}`));
5224
4592
  return;
5225
4593
  }
4594
+ const content = readFileSync(tmplPath, "utf-8");
5226
4595
  mkdirSync(dirname(outPath), { recursive: true });
5227
4596
  writeFileSync(outPath, render(content, data));
5228
4597
  }
@@ -5325,7 +4694,7 @@ async function scaffold(nameArg, options) {
5325
4694
  }
5326
4695
 
5327
4696
  // src/commands/build.ts
5328
- import { existsSync as existsSync6, readFileSync as readFileSync4, readdirSync as readdirSync2, renameSync, statSync as statSync2 } from "fs";
4697
+ import { existsSync as existsSync6, readFileSync as readFileSync4, readdirSync as readdirSync3, renameSync, statSync as statSync3 } from "fs";
5329
4698
  import { join as join6 } from "path";
5330
4699
  import { createHash } from "crypto";
5331
4700
 
@@ -7978,8 +7347,8 @@ function validate2(m) {
7978
7347
  errors2.push("author: must be an object with a name");
7979
7348
  if (!m.icon)
7980
7349
  errors2.push("icon: must be a string");
7981
- if (!["company", "project", "both"].includes(m.scope))
7982
- errors2.push('scope: must be "company", "project", or "both"');
7350
+ if (!["app", "project", "org", "any"].includes(m.scope))
7351
+ errors2.push('scope: must be "app", "project", "org", or "any"');
7983
7352
  if (!m.pages?.length)
7984
7353
  errors2.push("pages: must be a non-empty array");
7985
7354
  if (!m.navigation?.label)
@@ -8006,30 +7375,84 @@ function exists(dir) {
8006
7375
  }
8007
7376
 
8008
7377
  // src/lib/entry.ts
8009
- import { writeFileSync as writeFileSync3, mkdirSync as mkdirSync2, existsSync as existsSync4 } from "fs";
8010
- import { join as join4, basename, extname } from "path";
7378
+ import { writeFileSync as writeFileSync3, mkdirSync as mkdirSync2, existsSync as existsSync4, readdirSync, statSync } from "fs";
7379
+ import { join as join4, basename, extname, relative } from "path";
8011
7380
  function capitalize(s) {
8012
7381
  if (!s)
8013
7382
  return s;
8014
7383
  return s.split("-").map((w) => w.charAt(0).toUpperCase() + w.slice(1)).join("");
8015
7384
  }
8016
- function resolvePages(m, prefix) {
7385
+ function scanPagesDir(pagesDir) {
7386
+ const results = [];
7387
+ function walk(dir, routeSegments) {
7388
+ if (!existsSync4(dir))
7389
+ return;
7390
+ const entries = readdirSync(dir).sort();
7391
+ for (const entry of entries) {
7392
+ const fullPath = join4(dir, entry);
7393
+ const stat = statSync(fullPath);
7394
+ if (stat.isDirectory()) {
7395
+ const segment = entry.replace(/^\[(.+)\]$/, ":$1");
7396
+ walk(fullPath, [...routeSegments, segment]);
7397
+ } else if (stat.isFile() && entry.endsWith(".vue")) {
7398
+ const nameWithoutExt = entry.replace(/\.vue$/, "");
7399
+ const relFile = relative(join4(pagesDir, ".."), fullPath).replace(/\\/g, "/");
7400
+ let routePath;
7401
+ if (nameWithoutExt === "index") {
7402
+ routePath = routeSegments.join("/");
7403
+ } else {
7404
+ const segment = nameWithoutExt.replace(/^\[(.+)\]$/, ":$1");
7405
+ routePath = [...routeSegments, segment].join("/");
7406
+ }
7407
+ results.push({ filePath: relFile, routePath });
7408
+ }
7409
+ }
7410
+ }
7411
+ walk(pagesDir, []);
7412
+ return results;
7413
+ }
7414
+ function varNameFromFile(filePath) {
7415
+ let cleaned = filePath.replace(/^pages\//, "").replace(/\.vue$/, "");
7416
+ cleaned = cleaned.replace(/\[([^\]]+)\]/g, "$1");
7417
+ const segments = cleaned.split(/[\/-]/).filter(Boolean);
7418
+ const name = segments.map((s) => s.charAt(0).toUpperCase() + s.slice(1)).join("");
7419
+ return (name || "Index") + "Page";
7420
+ }
7421
+ function resolvePages(m, root, prefix) {
7422
+ const pagesDir = join4(root, "src", "pages");
7423
+ const fsPages = scanPagesDir(pagesDir);
7424
+ const fsMap = new Map;
7425
+ for (const fp of fsPages) {
7426
+ fsMap.set(fp.routePath, fp);
7427
+ }
8017
7428
  return m.pages.map((p) => {
8018
- let component = p.component;
8019
- if (!component) {
8020
- component = p.path === "" ? "pages/index.vue" : `pages/${p.path}.vue`;
7429
+ if (p.component) {
7430
+ const base = basename(p.component);
7431
+ const nameWithoutExt = base.replace(extname(base), "").replace(/[\[\]]/g, "");
7432
+ const dir = p.component.replace(/^pages\//, "").replace(/\/[^/]+$/, "");
7433
+ let varName;
7434
+ if (dir && dir !== p.component.replace(/^pages\//, "")) {
7435
+ varName = capitalize(dir.split("/").map((s) => s.replace(/[\[\]]/g, "")).join("-")) + capitalize(nameWithoutExt) + "Page";
7436
+ } else {
7437
+ varName = capitalize(nameWithoutExt) + "Page";
7438
+ }
7439
+ return { varName, importPath: prefix + p.component, path: p.path };
8021
7440
  }
8022
- let varName = "IndexPage";
8023
- if (p.path) {
8024
- let cleanPath = p.path.replace(/:/g, "");
8025
- if (p.component) {
8026
- let base = basename(p.component);
8027
- base = base.replace(extname(base), "").replace(/[\[\]]/g, "");
8028
- cleanPath = base;
7441
+ const fsPage = fsMap.get(p.path);
7442
+ if (fsPage) {
7443
+ const varName = varNameFromFile(fsPage.filePath);
7444
+ return { varName, importPath: prefix + fsPage.filePath, path: p.path };
7445
+ }
7446
+ const legacyComponent = p.path === "" ? "pages/index.vue" : `pages/${p.path}.vue`;
7447
+ const legacyFullPath = join4(root, "src", legacyComponent);
7448
+ if (existsSync4(legacyFullPath)) {
7449
+ let varName = "IndexPage";
7450
+ if (p.path) {
7451
+ varName = capitalize(p.path.replace(/[\/:]/g, "-").replace(/-+/g, "-")) + "Page";
8029
7452
  }
8030
- varName = capitalize(cleanPath) + "Page";
7453
+ return { varName, importPath: prefix + legacyComponent, path: p.path };
8031
7454
  }
8032
- return { varName, importPath: prefix + component, path: p.path };
7455
+ throw new Error(`[entry] Could not resolve component for page "${p.path}". ` + `Checked: manifest component field, filesystem scan of src/pages/, ` + `and legacy fallback at ${legacyComponent}. ` + `Ensure a .vue file exists for this route.`);
8033
7456
  });
8034
7457
  }
8035
7458
  function resolveWidgets(m, prefix) {
@@ -8048,7 +7471,7 @@ function resolveWidgets(m, prefix) {
8048
7471
  }
8049
7472
  function generate(root, m) {
8050
7473
  const pagePrefix = existsSync4(join4(root, "src", "pages")) ? "./" : "../";
8051
- const pages = resolvePages(m, pagePrefix);
7474
+ const pages = resolvePages(m, root, pagePrefix);
8052
7475
  const widgets = resolveWidgets(m, "../");
8053
7476
  const actionsPath = join4(root, "src", "actions.ts");
8054
7477
  const hasActions = existsSync4(actionsPath);
@@ -8104,7 +7527,7 @@ function writeEntry(root, m) {
8104
7527
  }
8105
7528
 
8106
7529
  // src/lib/agent.ts
8107
- import { readFileSync as readFileSync3, writeFileSync as writeFileSync4, readdirSync, existsSync as existsSync5 } from "fs";
7530
+ import { readFileSync as readFileSync3, writeFileSync as writeFileSync4, readdirSync as readdirSync2, existsSync as existsSync5 } from "fs";
8108
7531
  import { join as join5, extname as extname2, basename as basename2 } from "path";
8109
7532
  var AGENT_KEY = "construct-agent-obfuscate-v1";
8110
7533
  function encode(content) {
@@ -8120,7 +7543,7 @@ function readMdFiles(dir) {
8120
7543
  const result = {};
8121
7544
  if (!existsSync5(dir))
8122
7545
  return result;
8123
- for (const f of readdirSync(dir)) {
7546
+ for (const f of readdirSync2(dir)) {
8124
7547
  if (extname2(f) !== ".md")
8125
7548
  continue;
8126
7549
  result[basename2(f, ".md")] = readFileSync3(join5(dir, f), "utf-8");
@@ -8131,7 +7554,7 @@ function readJsonFiles(dir) {
8131
7554
  const result = {};
8132
7555
  if (!existsSync5(dir))
8133
7556
  return result;
8134
- for (const f of readdirSync(dir)) {
7557
+ for (const f of readdirSync2(dir)) {
8135
7558
  if (extname2(f) !== ".json")
8136
7559
  continue;
8137
7560
  result[basename2(f, ".json")] = readFileSync3(join5(dir, f), "utf-8");
@@ -8221,7 +7644,7 @@ async function build(options) {
8221
7644
  }
8222
7645
  runHook(m.hooks, "postBuild", root);
8223
7646
  const agentDir = join6(root, "agent");
8224
- if (existsSync6(agentDir) && statSync2(agentDir).isDirectory()) {
7647
+ if (existsSync6(agentDir) && statSync3(agentDir).isDirectory()) {
8225
7648
  const distDir2 = join6(root, "dist");
8226
7649
  bundleAgentDir(agentDir, distDir2);
8227
7650
  bundleAgentDir(agentDir, root);
@@ -8230,7 +7653,7 @@ async function build(options) {
8230
7653
  const expectedBundle = `space-${m.id}.iife.js`;
8231
7654
  let bundlePath = join6(distDir, expectedBundle);
8232
7655
  if (!existsSync6(bundlePath)) {
8233
- const matches = readdirSync2(distDir).filter((f) => f.startsWith("space-") && f.endsWith(".iife.js"));
7656
+ const matches = readdirSync3(distDir).filter((f) => f.startsWith("space-") && f.endsWith(".iife.js"));
8234
7657
  if (matches.length === 1) {
8235
7658
  renameSync(join6(distDir, matches[0]), bundlePath);
8236
7659
  const oldCSS = join6(distDir, matches[0].replace(".iife.js", ".css"));
@@ -8260,20 +7683,20 @@ async function build(options) {
8260
7683
  }
8261
7684
 
8262
7685
  // src/commands/dev.ts
8263
- import { existsSync as existsSync8, readFileSync as readFileSync6, cpSync, mkdirSync as mkdirSync3 } from "fs";
8264
- import { join as join10 } from "path";
7686
+ import { existsSync as existsSync7, readFileSync as readFileSync5 } from "fs";
7687
+ import { join as join9 } from "path";
8265
7688
  import { createHash as createHash2 } from "crypto";
8266
7689
 
8267
- // node_modules/chokidar/esm/index.js
8268
- import { stat as statcb } from "fs";
8269
- import { stat as stat3, readdir as readdir2 } from "fs/promises";
7690
+ // node_modules/chokidar/index.js
8270
7691
  import { EventEmitter } from "events";
8271
- import * as sysPath2 from "path";
7692
+ import { stat as statcb, Stats } from "fs";
7693
+ import { readdir as readdir2, stat as stat3 } from "fs/promises";
7694
+ import * as sp2 from "path";
8272
7695
 
8273
- // node_modules/readdirp/esm/index.js
8274
- import { stat, lstat, readdir, realpath } from "fs/promises";
7696
+ // node_modules/readdirp/index.js
7697
+ import { lstat, readdir, realpath, stat } from "fs/promises";
7698
+ import { join as pjoin, relative as prelative, resolve as presolve, sep as psep } from "path";
8275
7699
  import { Readable } from "stream";
8276
- import { resolve as presolve, relative as prelative, join as pjoin, sep as psep } from "path";
8277
7700
  var EntryTypes = {
8278
7701
  FILE_TYPE: "files",
8279
7702
  DIR_TYPE: "directories",
@@ -8329,6 +7752,20 @@ var normalizeFilter = (filter) => {
8329
7752
  };
8330
7753
 
8331
7754
  class ReaddirpStream extends Readable {
7755
+ parents;
7756
+ reading;
7757
+ parent;
7758
+ _stat;
7759
+ _maxDepth;
7760
+ _wantsDir;
7761
+ _wantsFile;
7762
+ _wantsEverything;
7763
+ _root;
7764
+ _isDirent;
7765
+ _statsProp;
7766
+ _rdOptions;
7767
+ _fileFilter;
7768
+ _directoryFilter;
8332
7769
  constructor(options = {}) {
8333
7770
  super({
8334
7771
  objectMode: true,
@@ -8345,7 +7782,7 @@ class ReaddirpStream extends Readable {
8345
7782
  } else {
8346
7783
  this._stat = statMethod;
8347
7784
  }
8348
- this._maxDepth = opts.depth ?? defaultOptions.depth;
7785
+ this._maxDepth = opts.depth != null && Number.isSafeInteger(opts.depth) ? opts.depth : defaultOptions.depth;
8349
7786
  this._wantsDir = type ? DIR_TYPES.has(type) : false;
8350
7787
  this._wantsFile = type ? FILE_TYPES.has(type) : false;
8351
7788
  this._wantsEverything = type === EntryTypes.EVERYTHING_TYPE;
@@ -8490,11 +7927,11 @@ function readdirp(root, options = {}) {
8490
7927
  return new ReaddirpStream(options);
8491
7928
  }
8492
7929
 
8493
- // node_modules/chokidar/esm/handler.js
8494
- import { watchFile, unwatchFile, watch as fs_watch } from "fs";
8495
- import { open, stat as stat2, lstat as lstat2, realpath as fsrealpath } from "fs/promises";
8496
- import * as sysPath from "path";
7930
+ // node_modules/chokidar/handler.js
7931
+ import { watch as fs_watch, unwatchFile, watchFile } from "fs";
7932
+ import { realpath as fsrealpath, lstat as lstat2, open, stat as stat2 } from "fs/promises";
8497
7933
  import { type as osType } from "os";
7934
+ import * as sp from "path";
8498
7935
  var STR_DATA = "data";
8499
7936
  var STR_END = "end";
8500
7937
  var STR_CLOSE = "close";
@@ -8786,7 +8223,7 @@ var binaryExtensions = new Set([
8786
8223
  "zip",
8787
8224
  "zipx"
8788
8225
  ]);
8789
- var isBinaryPath = (filePath) => binaryExtensions.has(sysPath.extname(filePath).slice(1).toLowerCase());
8226
+ var isBinaryPath = (filePath) => binaryExtensions.has(sp.extname(filePath).slice(1).toLowerCase());
8790
8227
  var foreach = (val, fn) => {
8791
8228
  if (val instanceof Set) {
8792
8229
  val.forEach(fn);
@@ -8824,7 +8261,7 @@ function createFsWatchInstance(path, options, listener, errHandler, emitRaw) {
8824
8261
  listener(path);
8825
8262
  emitRaw(rawEvent, evPath, { watchedPath: path });
8826
8263
  if (evPath && path !== evPath) {
8827
- fsWatchBroadcast(sysPath.resolve(path, evPath), KEY_LISTENERS, sysPath.join(path, evPath));
8264
+ fsWatchBroadcast(sp.resolve(path, evPath), KEY_LISTENERS, sp.join(path, evPath));
8828
8265
  }
8829
8266
  };
8830
8267
  try {
@@ -8939,17 +8376,19 @@ var setFsWatchFileListener = (path, fullPath, options, handlers) => {
8939
8376
  };
8940
8377
 
8941
8378
  class NodeFsHandler {
8379
+ fsw;
8380
+ _boundHandleError;
8942
8381
  constructor(fsW) {
8943
8382
  this.fsw = fsW;
8944
8383
  this._boundHandleError = (error2) => fsW._handleError(error2);
8945
8384
  }
8946
8385
  _watchWithNodeFs(path, listener) {
8947
8386
  const opts = this.fsw.options;
8948
- const directory = sysPath.dirname(path);
8949
- const basename4 = sysPath.basename(path);
8387
+ const directory = sp.dirname(path);
8388
+ const basename4 = sp.basename(path);
8950
8389
  const parent = this.fsw._getWatchedDir(directory);
8951
8390
  parent.add(basename4);
8952
- const absolutePath = sysPath.resolve(path);
8391
+ const absolutePath = sp.resolve(path);
8953
8392
  const options = {
8954
8393
  persistent: opts.persistent
8955
8394
  };
@@ -8976,8 +8415,8 @@ class NodeFsHandler {
8976
8415
  if (this.fsw.closed) {
8977
8416
  return;
8978
8417
  }
8979
- const dirname3 = sysPath.dirname(file);
8980
- const basename4 = sysPath.basename(file);
8418
+ const dirname3 = sp.dirname(file);
8419
+ const basename4 = sp.basename(file);
8981
8420
  const parent = this.fsw._getWatchedDir(dirname3);
8982
8421
  let prevStats = stats;
8983
8422
  if (parent.has(basename4))
@@ -9060,8 +8499,9 @@ class NodeFsHandler {
9060
8499
  this.fsw._symlinkPaths.set(full, true);
9061
8500
  }
9062
8501
  _handleRead(directory, initialAdd, wh, target, dir, depth, throttler) {
9063
- directory = sysPath.join(directory, "");
9064
- throttler = this.fsw._throttle("readdir", directory, 1000);
8502
+ directory = sp.join(directory, "");
8503
+ const throttleKey = target ? `${directory}:${target}` : directory;
8504
+ throttler = this.fsw._throttle("readdir", throttleKey, 1000);
9065
8505
  if (!throttler)
9066
8506
  return;
9067
8507
  const previous = this.fsw._getWatchedDir(wh.path);
@@ -9078,7 +8518,7 @@ class NodeFsHandler {
9078
8518
  return;
9079
8519
  }
9080
8520
  const item = entry.path;
9081
- let path = sysPath.join(directory, item);
8521
+ let path = sp.join(directory, item);
9082
8522
  current.add(item);
9083
8523
  if (entry.stats.isSymbolicLink() && await this._handleSymlink(entry, directory, path, item)) {
9084
8524
  return;
@@ -9089,7 +8529,7 @@ class NodeFsHandler {
9089
8529
  }
9090
8530
  if (item === target || !target && !previous.has(item)) {
9091
8531
  this.fsw._incrReadyCount();
9092
- path = sysPath.join(dir, sysPath.relative(dir, path));
8532
+ path = sp.join(dir, sp.relative(dir, path));
9093
8533
  this._addToNodeFs(path, initialAdd, wh, depth + 1);
9094
8534
  }
9095
8535
  }).on(EV.ERROR, this._boundHandleError);
@@ -9115,12 +8555,12 @@ class NodeFsHandler {
9115
8555
  });
9116
8556
  }
9117
8557
  async _handleDir(dir, stats, initialAdd, depth, target, wh, realpath2) {
9118
- const parentDir = this.fsw._getWatchedDir(sysPath.dirname(dir));
9119
- const tracked = parentDir.has(sysPath.basename(dir));
8558
+ const parentDir = this.fsw._getWatchedDir(sp.dirname(dir));
8559
+ const tracked = parentDir.has(sp.basename(dir));
9120
8560
  if (!(initialAdd && this.fsw.options.ignoreInitial) && !target && !tracked) {
9121
8561
  this.fsw._emit(EV.ADD_DIR, dir, stats);
9122
8562
  }
9123
- parentDir.add(sysPath.basename(dir));
8563
+ parentDir.add(sp.basename(dir));
9124
8564
  this.fsw._getWatchedDir(dir);
9125
8565
  let throttler;
9126
8566
  let closer;
@@ -9161,7 +8601,7 @@ class NodeFsHandler {
9161
8601
  const follow = this.fsw.options.followSymlinks;
9162
8602
  let closer;
9163
8603
  if (stats.isDirectory()) {
9164
- const absPath = sysPath.resolve(path);
8604
+ const absPath = sp.resolve(path);
9165
8605
  const targetPath = follow ? await fsrealpath(path) : path;
9166
8606
  if (this.fsw.closed)
9167
8607
  return;
@@ -9175,14 +8615,14 @@ class NodeFsHandler {
9175
8615
  const targetPath = follow ? await fsrealpath(path) : path;
9176
8616
  if (this.fsw.closed)
9177
8617
  return;
9178
- const parent = sysPath.dirname(wh.watchPath);
8618
+ const parent = sp.dirname(wh.watchPath);
9179
8619
  this.fsw._getWatchedDir(parent).add(wh.watchPath);
9180
8620
  this.fsw._emit(EV.ADD, wh.watchPath, stats);
9181
8621
  closer = await this._handleDir(parent, stats, initialAdd, depth, path, wh, targetPath);
9182
8622
  if (this.fsw.closed)
9183
8623
  return;
9184
8624
  if (targetPath !== undefined) {
9185
- this.fsw._symlinkPaths.set(sysPath.resolve(path), targetPath);
8625
+ this.fsw._symlinkPaths.set(sp.resolve(path), targetPath);
9186
8626
  }
9187
8627
  } else {
9188
8628
  closer = this._handleFile(wh.watchPath, stats, initialAdd);
@@ -9200,7 +8640,7 @@ class NodeFsHandler {
9200
8640
  }
9201
8641
  }
9202
8642
 
9203
- // node_modules/chokidar/esm/index.js
8643
+ // node_modules/chokidar/index.js
9204
8644
  /*! chokidar - MIT License (c) 2012 Paul Miller (paulmillr.com) */
9205
8645
  var SLASH = "/";
9206
8646
  var SLASH_SLASH = "//";
@@ -9208,7 +8648,7 @@ var ONE_DOT = ".";
9208
8648
  var TWO_DOTS = "..";
9209
8649
  var STRING_TYPE = "string";
9210
8650
  var BACK_SLASH_RE = /\\/g;
9211
- var DOUBLE_SLASH_RE = /\/\//;
8651
+ var DOUBLE_SLASH_RE = /\/\//g;
9212
8652
  var DOT_RE = /\..*\.(sw[px])$|~$|\.subl.*\.tmp/;
9213
8653
  var REPLACER_RE = /^\.[/\\]/;
9214
8654
  function arrify(item) {
@@ -9227,11 +8667,11 @@ function createPattern(matcher) {
9227
8667
  if (matcher.path === string)
9228
8668
  return true;
9229
8669
  if (matcher.recursive) {
9230
- const relative3 = sysPath2.relative(matcher.path, string);
9231
- if (!relative3) {
8670
+ const relative4 = sp2.relative(matcher.path, string);
8671
+ if (!relative4) {
9232
8672
  return false;
9233
8673
  }
9234
- return !relative3.startsWith("..") && !sysPath2.isAbsolute(relative3);
8674
+ return !relative4.startsWith("..") && !sp2.isAbsolute(relative4);
9235
8675
  }
9236
8676
  return false;
9237
8677
  };
@@ -9241,14 +8681,12 @@ function createPattern(matcher) {
9241
8681
  function normalizePath(path) {
9242
8682
  if (typeof path !== "string")
9243
8683
  throw new Error("string expected");
9244
- path = sysPath2.normalize(path);
8684
+ path = sp2.normalize(path);
9245
8685
  path = path.replace(/\\/g, "/");
9246
8686
  let prepend = false;
9247
8687
  if (path.startsWith("//"))
9248
8688
  prepend = true;
9249
- const DOUBLE_SLASH_RE2 = /\/\//;
9250
- while (path.match(DOUBLE_SLASH_RE2))
9251
- path = path.replace(DOUBLE_SLASH_RE2, "/");
8689
+ path = path.replace(DOUBLE_SLASH_RE, "/");
9252
8690
  if (prepend)
9253
8691
  path = "/" + path;
9254
8692
  return path;
@@ -9289,31 +8727,32 @@ var toUnix = (string) => {
9289
8727
  if (str.startsWith(SLASH_SLASH)) {
9290
8728
  prepend = true;
9291
8729
  }
9292
- while (str.match(DOUBLE_SLASH_RE)) {
9293
- str = str.replace(DOUBLE_SLASH_RE, SLASH);
9294
- }
8730
+ str = str.replace(DOUBLE_SLASH_RE, SLASH);
9295
8731
  if (prepend) {
9296
8732
  str = SLASH + str;
9297
8733
  }
9298
8734
  return str;
9299
8735
  };
9300
- var normalizePathToUnix = (path) => toUnix(sysPath2.normalize(toUnix(path)));
8736
+ var normalizePathToUnix = (path) => toUnix(sp2.normalize(toUnix(path)));
9301
8737
  var normalizeIgnored = (cwd = "") => (path) => {
9302
8738
  if (typeof path === "string") {
9303
- return normalizePathToUnix(sysPath2.isAbsolute(path) ? path : sysPath2.join(cwd, path));
8739
+ return normalizePathToUnix(sp2.isAbsolute(path) ? path : sp2.join(cwd, path));
9304
8740
  } else {
9305
8741
  return path;
9306
8742
  }
9307
8743
  };
9308
8744
  var getAbsolutePath = (path, cwd) => {
9309
- if (sysPath2.isAbsolute(path)) {
8745
+ if (sp2.isAbsolute(path)) {
9310
8746
  return path;
9311
8747
  }
9312
- return sysPath2.join(cwd, path);
8748
+ return sp2.join(cwd, path);
9313
8749
  };
9314
8750
  var EMPTY_SET = Object.freeze(new Set);
9315
8751
 
9316
8752
  class DirEntry {
8753
+ path;
8754
+ _removeWatcher;
8755
+ items;
9317
8756
  constructor(dir, removeWatcher) {
9318
8757
  this.path = dir;
9319
8758
  this._removeWatcher = removeWatcher;
@@ -9338,7 +8777,7 @@ class DirEntry {
9338
8777
  await readdir2(dir);
9339
8778
  } catch (err) {
9340
8779
  if (this._removeWatcher) {
9341
- this._removeWatcher(sysPath2.dirname(dir), sysPath2.basename(dir));
8780
+ this._removeWatcher(sp2.dirname(dir), sp2.basename(dir));
9342
8781
  }
9343
8782
  }
9344
8783
  }
@@ -9366,12 +8805,19 @@ var STAT_METHOD_F = "stat";
9366
8805
  var STAT_METHOD_L = "lstat";
9367
8806
 
9368
8807
  class WatchHelper {
8808
+ fsw;
8809
+ path;
8810
+ watchPath;
8811
+ fullWatchPath;
8812
+ dirParts;
8813
+ followSymlinks;
8814
+ statMethod;
9369
8815
  constructor(path, follow, fsw) {
9370
8816
  this.fsw = fsw;
9371
8817
  const watchPath = path;
9372
8818
  this.path = path = path.replace(REPLACER_RE, "");
9373
8819
  this.watchPath = watchPath;
9374
- this.fullWatchPath = sysPath2.resolve(watchPath);
8820
+ this.fullWatchPath = sp2.resolve(watchPath);
9375
8821
  this.dirParts = [];
9376
8822
  this.dirParts.forEach((parts) => {
9377
8823
  if (parts.length > 1)
@@ -9381,7 +8827,7 @@ class WatchHelper {
9381
8827
  this.statMethod = follow ? STAT_METHOD_F : STAT_METHOD_L;
9382
8828
  }
9383
8829
  entryPath(entry) {
9384
- return sysPath2.join(this.watchPath, sysPath2.relative(this.watchPath, entry.fullPath));
8830
+ return sp2.join(this.watchPath, sp2.relative(this.watchPath, entry.fullPath));
9385
8831
  }
9386
8832
  filterPath(entry) {
9387
8833
  const { stats } = entry;
@@ -9396,6 +8842,24 @@ class WatchHelper {
9396
8842
  }
9397
8843
 
9398
8844
  class FSWatcher extends EventEmitter {
8845
+ closed;
8846
+ options;
8847
+ _closers;
8848
+ _ignoredPaths;
8849
+ _throttled;
8850
+ _streams;
8851
+ _symlinkPaths;
8852
+ _watched;
8853
+ _pendingWrites;
8854
+ _pendingUnlinks;
8855
+ _readyCount;
8856
+ _emitReady;
8857
+ _closePromise;
8858
+ _userIgnored;
8859
+ _readyEmitted;
8860
+ _emitRaw;
8861
+ _boundRemove;
8862
+ _nodeFsHandler;
9399
8863
  constructor(_opts = {}) {
9400
8864
  super();
9401
8865
  this.closed = false;
@@ -9504,7 +8968,7 @@ class FSWatcher extends EventEmitter {
9504
8968
  return;
9505
8969
  results.forEach((item) => {
9506
8970
  if (item)
9507
- this.add(sysPath2.dirname(item), sysPath2.basename(_origAdd || item));
8971
+ this.add(sp2.dirname(item), sp2.basename(_origAdd || item));
9508
8972
  });
9509
8973
  });
9510
8974
  return this;
@@ -9515,10 +8979,10 @@ class FSWatcher extends EventEmitter {
9515
8979
  const paths = unifyPaths(paths_);
9516
8980
  const { cwd } = this.options;
9517
8981
  paths.forEach((path) => {
9518
- if (!sysPath2.isAbsolute(path) && !this._closers.has(path)) {
8982
+ if (!sp2.isAbsolute(path) && !this._closers.has(path)) {
9519
8983
  if (cwd)
9520
- path = sysPath2.join(cwd, path);
9521
- path = sysPath2.resolve(path);
8984
+ path = sp2.join(cwd, path);
8985
+ path = sp2.resolve(path);
9522
8986
  }
9523
8987
  this._closePath(path);
9524
8988
  this._addIgnoredPath(path);
@@ -9562,7 +9026,7 @@ class FSWatcher extends EventEmitter {
9562
9026
  getWatched() {
9563
9027
  const watchList = {};
9564
9028
  this._watched.forEach((entry, dir) => {
9565
- const key = this.options.cwd ? sysPath2.relative(this.options.cwd, dir) : dir;
9029
+ const key = this.options.cwd ? sp2.relative(this.options.cwd, dir) : dir;
9566
9030
  const index = key || ONE_DOT;
9567
9031
  watchList[index] = entry.getChildren().sort();
9568
9032
  });
@@ -9578,9 +9042,9 @@ class FSWatcher extends EventEmitter {
9578
9042
  return;
9579
9043
  const opts = this.options;
9580
9044
  if (isWindows)
9581
- path = sysPath2.normalize(path);
9045
+ path = sp2.normalize(path);
9582
9046
  if (opts.cwd)
9583
- path = sysPath2.relative(opts.cwd, path);
9047
+ path = sp2.relative(opts.cwd, path);
9584
9048
  const args = [path];
9585
9049
  if (stats != null)
9586
9050
  args.push(stats);
@@ -9631,7 +9095,7 @@ class FSWatcher extends EventEmitter {
9631
9095
  return this;
9632
9096
  }
9633
9097
  if (opts.alwaysStat && stats === undefined && (event === EVENTS.ADD || event === EVENTS.ADD_DIR || event === EVENTS.CHANGE)) {
9634
- const fullPath = opts.cwd ? sysPath2.join(opts.cwd, path) : path;
9098
+ const fullPath = opts.cwd ? sp2.join(opts.cwd, path) : path;
9635
9099
  let stats2;
9636
9100
  try {
9637
9101
  stats2 = await stat3(fullPath);
@@ -9687,8 +9151,8 @@ class FSWatcher extends EventEmitter {
9687
9151
  const pollInterval = awf.pollInterval;
9688
9152
  let timeoutHandler;
9689
9153
  let fullPath = path;
9690
- if (this.options.cwd && !sysPath2.isAbsolute(path)) {
9691
- fullPath = sysPath2.join(this.options.cwd, path);
9154
+ if (this.options.cwd && !sp2.isAbsolute(path)) {
9155
+ fullPath = sp2.join(this.options.cwd, path);
9692
9156
  }
9693
9157
  const now = new Date;
9694
9158
  const writes = this._pendingWrites;
@@ -9745,7 +9209,7 @@ class FSWatcher extends EventEmitter {
9745
9209
  return new WatchHelper(path, this.options.followSymlinks, this);
9746
9210
  }
9747
9211
  _getWatchedDir(directory) {
9748
- const dir = sysPath2.resolve(directory);
9212
+ const dir = sp2.resolve(directory);
9749
9213
  if (!this._watched.has(dir))
9750
9214
  this._watched.set(dir, new DirEntry(dir, this._boundRemove));
9751
9215
  return this._watched.get(dir);
@@ -9756,8 +9220,8 @@ class FSWatcher extends EventEmitter {
9756
9220
  return Boolean(Number(stats.mode) & 256);
9757
9221
  }
9758
9222
  _remove(directory, item, isDirectory) {
9759
- const path = sysPath2.join(directory, item);
9760
- const fullPath = sysPath2.resolve(path);
9223
+ const path = sp2.join(directory, item);
9224
+ const fullPath = sp2.resolve(path);
9761
9225
  isDirectory = isDirectory != null ? isDirectory : this._watched.has(path) || this._watched.has(fullPath);
9762
9226
  if (!this._throttle("remove", path, 100))
9763
9227
  return;
@@ -9775,7 +9239,7 @@ class FSWatcher extends EventEmitter {
9775
9239
  }
9776
9240
  let relPath = path;
9777
9241
  if (this.options.cwd)
9778
- relPath = sysPath2.relative(this.options.cwd, path);
9242
+ relPath = sp2.relative(this.options.cwd, path);
9779
9243
  if (this.options.awaitWriteFinish && this._pendingWrites.has(relPath)) {
9780
9244
  const event = this._pendingWrites.get(relPath).cancelWait();
9781
9245
  if (event === EVENTS.ADD)
@@ -9790,8 +9254,8 @@ class FSWatcher extends EventEmitter {
9790
9254
  }
9791
9255
  _closePath(path) {
9792
9256
  this._closeFile(path);
9793
- const dir = sysPath2.dirname(path);
9794
- this._getWatchedDir(dir).remove(sysPath2.basename(path));
9257
+ const dir = sp2.dirname(path);
9258
+ this._getWatchedDir(dir).remove(sp2.basename(path));
9795
9259
  }
9796
9260
  _closeFile(path) {
9797
9261
  const closers = this._closers.get(path);
@@ -9834,55 +9298,11 @@ function watch(paths, options = {}) {
9834
9298
  return watcher;
9835
9299
  }
9836
9300
 
9837
- // src/lib/appdir.ts
9838
- import { join as join9 } from "path";
9839
- import { homedir } from "os";
9840
- import { platform as platform2 } from "process";
9841
- import { existsSync as existsSync7, readFileSync as readFileSync5 } from "fs";
9842
- function dataDir() {
9843
- if (process.env.CONSTRUCT_DATA_DIR)
9844
- return process.env.CONSTRUCT_DATA_DIR;
9845
- const home = homedir();
9846
- switch (platform2) {
9847
- case "darwin":
9848
- return join9(home, "Library", "Application Support", "Construct");
9849
- case "win32": {
9850
- const appData = process.env.APPDATA || join9(home, "AppData", "Roaming");
9851
- return join9(appData, "Construct");
9852
- }
9853
- default: {
9854
- const xdg = process.env.XDG_DATA_HOME || join9(home, ".local", "share");
9855
- return join9(xdg, "construct");
9856
- }
9857
- }
9858
- }
9859
- function activeProfileId() {
9860
- try {
9861
- const profilesPath = join9(dataDir(), "profiles.json");
9862
- if (!existsSync7(profilesPath))
9863
- return null;
9864
- const data = JSON.parse(readFileSync5(profilesPath, "utf-8"));
9865
- return data?.active_profile || null;
9866
- } catch {
9867
- return null;
9868
- }
9869
- }
9870
- function spacesDir() {
9871
- const profileId = activeProfileId();
9872
- if (profileId) {
9873
- return join9(dataDir(), "profiles", profileId, "spaces");
9874
- }
9875
- return join9(dataDir(), "spaces");
9876
- }
9877
- function spaceDir(spaceId) {
9878
- return join9(spacesDir(), spaceId);
9879
- }
9880
-
9881
9301
  // src/commands/dev.ts
9882
9302
  function getEntryWatchPaths(root) {
9883
9303
  return [
9884
- join10(root, MANIFEST_FILE),
9885
- join10(root, "src", "actions.ts")
9304
+ join9(root, MANIFEST_FILE),
9305
+ join9(root, "src", "actions.ts")
9886
9306
  ];
9887
9307
  }
9888
9308
  async function dev() {
@@ -9921,14 +9341,14 @@ async function dev() {
9921
9341
  }
9922
9342
  console.log(source_default.blue("Actions changed \u2014 entry regenerated"));
9923
9343
  });
9924
- const distDir = join10(root, "dist");
9925
- const bundleFile = join10(distDir, `space-${m.id}.iife.js`);
9344
+ const distDir = join9(root, "dist");
9345
+ const bundleFile = join9(distDir, `space-${m.id}.iife.js`);
9926
9346
  let lastChecksum = "";
9927
9347
  const distWatcher = watch(bundleFile, { ignoreInitial: false });
9928
9348
  distWatcher.on("all", () => {
9929
- if (!existsSync8(bundleFile))
9349
+ if (!existsSync7(bundleFile))
9930
9350
  return;
9931
- const bundleData = readFileSync6(bundleFile);
9351
+ const bundleData = readFileSync5(bundleFile);
9932
9352
  const checksum = createHash2("sha256").update(bundleData).digest("hex");
9933
9353
  if (checksum === lastChecksum)
9934
9354
  return;
@@ -9940,15 +9360,7 @@ async function dev() {
9940
9360
  hostApiVersion: "0.2.0",
9941
9361
  builtAt: new Date().toISOString()
9942
9362
  });
9943
- try {
9944
- const installDir = spaceDir(m.id);
9945
- mkdirSync3(installDir, { recursive: true });
9946
- cpSync(distDir, installDir, { recursive: true });
9947
- console.log(source_default.green(`Built + installed \u2192 ${installDir} (${(bundleData.length / 1024).toFixed(1)} KB)`));
9948
- } catch (err) {
9949
- console.log(source_default.green(`Built \u2192 dist/ (${(bundleData.length / 1024).toFixed(1)} KB)`));
9950
- console.log(source_default.yellow(` Install failed: ${err.message}`));
9951
- }
9363
+ console.log(source_default.green(`Built \u2192 dist/ (${(bundleData.length / 1024).toFixed(1)} KB)`));
9952
9364
  });
9953
9365
  console.log(source_default.green("Watching for changes... (Ctrl+C to stop)"));
9954
9366
  console.log(source_default.dim("Use the Preview button in Construct to open the Space Runner"));
@@ -9956,8 +9368,41 @@ async function dev() {
9956
9368
  }
9957
9369
 
9958
9370
  // src/commands/run.ts
9959
- import { existsSync as existsSync9, cpSync as cpSync2, mkdirSync as mkdirSync4 } from "fs";
9371
+ import { existsSync as existsSync8, cpSync, mkdirSync as mkdirSync3 } from "fs";
9960
9372
  import { join as join11 } from "path";
9373
+
9374
+ // src/lib/appdir.ts
9375
+ import { join as join10 } from "path";
9376
+ import { homedir } from "os";
9377
+ import { platform } from "process";
9378
+ function dataDir() {
9379
+ if (process.env.CONSTRUCT_DATA_DIR)
9380
+ return process.env.CONSTRUCT_DATA_DIR;
9381
+ const home = homedir();
9382
+ switch (platform) {
9383
+ case "darwin":
9384
+ return join10(home, "Library", "Application Support", "Construct");
9385
+ case "win32": {
9386
+ const appData = process.env.APPDATA || join10(home, "AppData", "Roaming");
9387
+ return join10(appData, "Construct");
9388
+ }
9389
+ default: {
9390
+ const xdg = process.env.XDG_DATA_HOME || join10(home, ".local", "share");
9391
+ return join10(xdg, "construct");
9392
+ }
9393
+ }
9394
+ }
9395
+ function spacesDir() {
9396
+ return join10(dataDir(), "spaces");
9397
+ }
9398
+ function profilesDir() {
9399
+ return join10(dataDir(), "profiles");
9400
+ }
9401
+ function spaceDir(spaceId) {
9402
+ return join10(spacesDir(), spaceId);
9403
+ }
9404
+
9405
+ // src/commands/run.ts
9961
9406
  function install() {
9962
9407
  const root = process.cwd();
9963
9408
  if (!exists(root)) {
@@ -9965,83 +9410,75 @@ function install() {
9965
9410
  process.exit(1);
9966
9411
  }
9967
9412
  const distDir = join11(root, "dist");
9968
- if (!existsSync9(distDir)) {
9413
+ if (!existsSync8(distDir)) {
9969
9414
  console.error(source_default.red("No dist/ directory found. Run 'construct build' first."));
9970
9415
  process.exit(1);
9971
9416
  }
9972
9417
  const m = read(root);
9973
9418
  const agentDir = join11(root, "agent");
9974
- if (existsSync9(agentDir)) {
9419
+ if (existsSync8(agentDir)) {
9975
9420
  bundleAgentDir(agentDir, distDir);
9976
9421
  }
9977
9422
  const installDir = spaceDir(m.id);
9978
- mkdirSync4(installDir, { recursive: true });
9979
- cpSync2(distDir, installDir, { recursive: true });
9423
+ mkdirSync3(installDir, { recursive: true });
9424
+ cpSync(distDir, installDir, { recursive: true });
9980
9425
  console.log(source_default.green(`Installed ${m.name} \u2192 ${installDir}`));
9981
9426
  console.log(source_default.dim(" Restart Construct to load the updated space."));
9982
9427
  }
9983
9428
 
9984
9429
  // src/commands/publish.ts
9985
- import { readFileSync as readFileSync8, writeFileSync as writeFileSync6, statSync as statSync4, unlinkSync as unlinkSync2 } from "fs";
9430
+ import { readFileSync as readFileSync7, writeFileSync as writeFileSync6, statSync as statSync6, unlinkSync as unlinkSync2 } from "fs";
9986
9431
  import { join as join14, basename as basename6 } from "path";
9987
9432
 
9988
9433
  // src/lib/auth.ts
9989
- import { readFileSync as readFileSync7, writeFileSync as writeFileSync5, mkdirSync as mkdirSync5, unlinkSync, existsSync as existsSync10 } from "fs";
9434
+ import { readFileSync as readFileSync6, writeFileSync as writeFileSync5, mkdirSync as mkdirSync4, unlinkSync, existsSync as existsSync9, readdirSync as readdirSync4, statSync as statSync4 } from "fs";
9990
9435
  import { join as join12, dirname as dirname4 } from "path";
9991
9436
  var CREDENTIALS_FILE = "credentials.json";
9992
- var APP_PROFILES_FILE = "profiles.json";
9993
- var DEFAULT_PORTAL = "https://developer.construct.space";
9437
+ var DEFAULT_PORTAL = "https://my.construct.space/api/developer";
9438
+ function listDesktopProfiles() {
9439
+ const dir = profilesDir();
9440
+ if (!existsSync9(dir))
9441
+ return [];
9442
+ const results = [];
9443
+ for (const entry of readdirSync4(dir)) {
9444
+ const full = join12(dir, entry);
9445
+ try {
9446
+ if (!statSync4(full).isDirectory())
9447
+ continue;
9448
+ const authPath = join12(full, "auth.json");
9449
+ if (!existsSync9(authPath))
9450
+ continue;
9451
+ const data = JSON.parse(readFileSync6(authPath, "utf-8"));
9452
+ if (!data.token)
9453
+ continue;
9454
+ if (data.authenticated !== undefined && !data.authenticated)
9455
+ continue;
9456
+ results.push({
9457
+ id: entry,
9458
+ token: data.token,
9459
+ user: data.user,
9460
+ updatedAt: data.updated_at,
9461
+ authenticated: true
9462
+ });
9463
+ } catch {}
9464
+ }
9465
+ return results;
9466
+ }
9994
9467
  function credentialsPath() {
9995
9468
  return join12(dataDir(), CREDENTIALS_FILE);
9996
9469
  }
9997
- function appProfilesPath() {
9998
- return join12(dataDir(), APP_PROFILES_FILE);
9999
- }
10000
- function appProfileAuthPath(profileId) {
10001
- return join12(dataDir(), "profiles", profileId, "auth.json");
10002
- }
10003
- function loadFromApp() {
10004
- try {
10005
- const profilesPath = appProfilesPath();
10006
- if (!existsSync10(profilesPath))
10007
- return null;
10008
- const profiles = JSON.parse(readFileSync7(profilesPath, "utf-8"));
10009
- if (!profiles?.active_profile)
10010
- return null;
10011
- const authPath = appProfileAuthPath(profiles.active_profile);
10012
- if (!existsSync10(authPath))
10013
- return null;
10014
- const auth = JSON.parse(readFileSync7(authPath, "utf-8"));
10015
- if (!auth?.authenticated || !auth?.token)
10016
- return null;
10017
- return {
10018
- token: auth.token,
10019
- portal: DEFAULT_PORTAL,
10020
- user: {
10021
- id: auth.user.id,
10022
- name: auth.user.name || [auth.user.first_name, auth.user.last_name].filter(Boolean).join(" ") || auth.user.email,
10023
- email: auth.user.email
10024
- }
10025
- };
10026
- } catch {
10027
- return null;
10028
- }
10029
- }
10030
9470
  function store(creds) {
10031
9471
  const path = credentialsPath();
10032
- mkdirSync5(dirname4(path), { recursive: true });
9472
+ mkdirSync4(dirname4(path), { recursive: true });
10033
9473
  writeFileSync5(path, JSON.stringify(creds, null, 2) + `
10034
9474
  `, { mode: 384 });
10035
9475
  }
10036
9476
  function load2() {
10037
- const fromApp = loadFromApp();
10038
- if (fromApp)
10039
- return fromApp;
10040
9477
  const path = credentialsPath();
10041
- if (!existsSync10(path)) {
10042
- throw new Error("not logged in \u2014 run 'construct login' first (or sign in to the Construct app)");
9478
+ if (!existsSync9(path)) {
9479
+ throw new Error("not logged in \u2014 run 'construct login' first");
10043
9480
  }
10044
- const data = JSON.parse(readFileSync7(path, "utf-8"));
9481
+ const data = JSON.parse(readFileSync6(path, "utf-8"));
10045
9482
  if (!data.token) {
10046
9483
  throw new Error("not logged in \u2014 run 'construct login' first");
10047
9484
  }
@@ -10057,12 +9494,12 @@ function isAuthenticated() {
10057
9494
  }
10058
9495
  function clear() {
10059
9496
  const path = credentialsPath();
10060
- if (existsSync10(path))
9497
+ if (existsSync9(path))
10061
9498
  unlinkSync(path);
10062
9499
  }
10063
9500
 
10064
9501
  // src/lib/pack.ts
10065
- import { readdirSync as readdirSync3, statSync as statSync3, existsSync as existsSync11 } from "fs";
9502
+ import { readdirSync as readdirSync5, statSync as statSync5, existsSync as existsSync10 } from "fs";
10066
9503
  import { join as join13 } from "path";
10067
9504
  import { tmpdir } from "os";
10068
9505
  import { execSync as execSync3 } from "child_process";
@@ -10101,11 +9538,11 @@ async function packSource(root) {
10101
9538
  const tarballPath = join13(tmpdir(), `space-source-${Date.now()}.tar.gz`);
10102
9539
  const entries = [];
10103
9540
  for (const name of allowedRootFiles) {
10104
- if (existsSync11(join13(root, name)))
9541
+ if (existsSync10(join13(root, name)))
10105
9542
  entries.push(name);
10106
9543
  }
10107
- for (const entry of readdirSync3(root)) {
10108
- if (statSync3(join13(root, entry)).isDirectory())
9544
+ for (const entry of readdirSync5(root)) {
9545
+ if (statSync5(join13(root, entry)).isDirectory())
10109
9546
  continue;
10110
9547
  if (allowedRootFiles.includes(entry))
10111
9548
  continue;
@@ -10115,17 +9552,17 @@ async function packSource(root) {
10115
9552
  entries.push(entry);
10116
9553
  }
10117
9554
  for (const dir of allowedDirs) {
10118
- if (existsSync11(join13(root, dir)))
9555
+ if (existsSync10(join13(root, dir)))
10119
9556
  entries.push(dir);
10120
9557
  }
10121
- const validEntries = entries.filter((e) => existsSync11(join13(root, e)));
9558
+ const validEntries = entries.filter((e) => existsSync10(join13(root, e)));
10122
9559
  if (validEntries.length === 0) {
10123
9560
  throw new Error("No files to pack");
10124
9561
  }
10125
9562
  const excludes = "--exclude=node_modules --exclude=dist --exclude=.git --exclude=*.env --exclude=*.log --exclude=*.lock --exclude=*.lockb";
10126
9563
  const cmd = `tar czf "${tarballPath}" ${excludes} ${validEntries.join(" ")}`;
10127
9564
  execSync3(cmd, { cwd: root });
10128
- const size = statSync3(tarballPath).size;
9565
+ const size = statSync5(tarballPath).size;
10129
9566
  if (size > MAX_SIZE) {
10130
9567
  throw new Error(`Source exceeds maximum size of ${MAX_SIZE / 1024 / 1024}MB`);
10131
9568
  }
@@ -10136,10 +9573,10 @@ async function packSource(root) {
10136
9573
  async function uploadSource(portalURL, token, tarballPath, m) {
10137
9574
  const formData = new FormData;
10138
9575
  formData.append("manifest", JSON.stringify(m));
10139
- const fileData = readFileSync8(tarballPath);
9576
+ const fileData = readFileSync7(tarballPath);
10140
9577
  const blob = new Blob([fileData]);
10141
9578
  formData.append("source", blob, basename6(tarballPath));
10142
- const resp = await fetch(`${portalURL}/api/publish`, {
9579
+ const resp = await fetch(`${portalURL}/publish`, {
10143
9580
  method: "POST",
10144
9581
  headers: { Authorization: `Bearer ${token}` },
10145
9582
  body: formData
@@ -10148,6 +9585,16 @@ async function uploadSource(portalURL, token, tarballPath, m) {
10148
9585
  if (resp.status === 401) {
10149
9586
  throw new Error("authentication failed \u2014 run 'construct login' to re-authenticate");
10150
9587
  }
9588
+ if (resp.status === 403) {
9589
+ let msg = result.error || "You are not the owner of this space";
9590
+ if (result.owner_user_id) {
9591
+ msg += `
9592
+ Current owner: ${result.owner_user_id}`;
9593
+ }
9594
+ msg += `
9595
+ Fork to a new space_id to publish your own version.`;
9596
+ throw new Error(msg);
9597
+ }
10151
9598
  if (resp.status >= 400) {
10152
9599
  const msg = result.error || result.errors?.join("; ") || `server returned ${resp.status}`;
10153
9600
  throw new Error(msg);
@@ -10160,7 +9607,7 @@ function setVersionInFiles(root, oldVer, newVer) {
10160
9607
  for (const file of ["package.json", "space.manifest.json"]) {
10161
9608
  const path = join14(root, file);
10162
9609
  try {
10163
- const data = readFileSync8(path, "utf-8");
9610
+ const data = readFileSync7(path, "utf-8");
10164
9611
  writeFileSync6(path, data.replace(oldStr, newStr));
10165
9612
  } catch {}
10166
9613
  }
@@ -10182,24 +9629,6 @@ async function publish(options) {
10182
9629
  console.log(source_default.dim(" Run 'construct login' to authenticate."));
10183
9630
  process.exit(1);
10184
9631
  }
10185
- const publishers = creds.publishers || [];
10186
- if (publishers.length === 0) {
10187
- console.log(source_default.yellow(" No publisher linked to this session. The upload will likely be rejected."));
10188
- console.log(source_default.dim(" Run 'construct login' after enrolling at developer.construct.space."));
10189
- } else {
10190
- const primary = publishers[0];
10191
- const kindLabel = primary.kind === "org" ? source_default.cyan("org") : primary.kind === "user" ? source_default.blue("personal") : source_default.dim("legacy");
10192
- console.log(source_default.dim(` Publishing as ${primary.name} (${kindLabel})`));
10193
- if (publishers.length > 1 && !yes) {
10194
- const proceed = await dist_default4({
10195
- message: `You have ${publishers.length} publisher identities. Publish under ${primary.name}?`
10196
- });
10197
- if (!proceed) {
10198
- console.log("Cancelled.");
10199
- return;
10200
- }
10201
- }
10202
- }
10203
9632
  const status = gitSafe(root, "status", "--porcelain");
10204
9633
  if (status) {
10205
9634
  console.log(source_default.yellow("You have uncommitted changes."));
@@ -10283,7 +9712,7 @@ async function publish(options) {
10283
9712
  let tarballPath;
10284
9713
  try {
10285
9714
  tarballPath = await packSource(root);
10286
- const size = statSync4(tarballPath).size;
9715
+ const size = statSync6(tarballPath).size;
10287
9716
  spinner.succeed(`Source packed (${formatBytes(size)})`);
10288
9717
  } catch (err) {
10289
9718
  spinner.fail("Pack failed");
@@ -10322,7 +9751,7 @@ async function publish(options) {
10322
9751
  }
10323
9752
 
10324
9753
  // src/commands/validate.ts
10325
- import { existsSync as existsSync12, readFileSync as readFileSync9 } from "fs";
9754
+ import { existsSync as existsSync11, readFileSync as readFileSync8 } from "fs";
10326
9755
  import { join as join15 } from "path";
10327
9756
  function validate3() {
10328
9757
  const root = process.cwd();
@@ -10342,21 +9771,21 @@ function validate3() {
10342
9771
  for (const page of m.pages) {
10343
9772
  const component = page.component || (page.path === "" ? "pages/index.vue" : `pages/${page.path}.vue`);
10344
9773
  const fullPath = join15(root, "src", component);
10345
- if (!existsSync12(fullPath)) {
9774
+ if (!existsSync11(fullPath)) {
10346
9775
  console.log(source_default.yellow(` \u26A0 Page component not found: src/${component}`));
10347
9776
  warnings++;
10348
9777
  }
10349
9778
  }
10350
9779
  if (m.agent) {
10351
9780
  const agentPath = join15(root, m.agent);
10352
- if (!existsSync12(agentPath)) {
9781
+ if (!existsSync11(agentPath)) {
10353
9782
  console.log(source_default.yellow(` \u26A0 Agent config not found: ${m.agent}`));
10354
9783
  warnings++;
10355
9784
  }
10356
9785
  }
10357
9786
  const pkgPath = join15(root, "package.json");
10358
- if (existsSync12(pkgPath)) {
10359
- const pkg = JSON.parse(readFileSync9(pkgPath, "utf-8"));
9787
+ if (existsSync11(pkgPath)) {
9788
+ const pkg = JSON.parse(readFileSync8(pkgPath, "utf-8"));
10360
9789
  if (pkg.version && pkg.version !== m.version) {
10361
9790
  console.log(source_default.yellow(` \u26A0 Version mismatch: manifest=${m.version} package.json=${pkg.version}`));
10362
9791
  warnings++;
@@ -10371,7 +9800,7 @@ function validate3() {
10371
9800
 
10372
9801
  // src/commands/check.ts
10373
9802
  import { execSync as execSync4 } from "child_process";
10374
- import { existsSync as existsSync13, readFileSync as readFileSync10 } from "fs";
9803
+ import { existsSync as existsSync12, readFileSync as readFileSync9 } from "fs";
10375
9804
  import { join as join16 } from "path";
10376
9805
  function check() {
10377
9806
  const root = process.cwd();
@@ -10391,18 +9820,18 @@ function check() {
10391
9820
  let warnings = 0;
10392
9821
  for (const page of m.pages) {
10393
9822
  const component = page.component || (page.path === "" ? "pages/index.vue" : `pages/${page.path}.vue`);
10394
- if (!existsSync13(join16(root, "src", component))) {
9823
+ if (!existsSync12(join16(root, "src", component))) {
10395
9824
  console.log(source_default.yellow(` \u26A0 Page not found: src/${component}`));
10396
9825
  warnings++;
10397
9826
  }
10398
9827
  }
10399
- if (m.agent && !existsSync13(join16(root, m.agent))) {
9828
+ if (m.agent && !existsSync12(join16(root, m.agent))) {
10400
9829
  console.log(source_default.yellow(` \u26A0 Agent config not found: ${m.agent}`));
10401
9830
  warnings++;
10402
9831
  }
10403
9832
  const pkgPath = join16(root, "package.json");
10404
- if (existsSync13(pkgPath)) {
10405
- const pkg = JSON.parse(readFileSync10(pkgPath, "utf-8"));
9833
+ if (existsSync12(pkgPath)) {
9834
+ const pkg = JSON.parse(readFileSync9(pkgPath, "utf-8"));
10406
9835
  if (pkg.version && pkg.version !== m.version) {
10407
9836
  console.log(source_default.yellow(` \u26A0 Version mismatch: manifest=${m.version} package.json=${pkg.version}`));
10408
9837
  warnings++;
@@ -10433,7 +9862,7 @@ function check() {
10433
9862
  }
10434
9863
 
10435
9864
  // src/commands/clean.ts
10436
- import { rmSync, existsSync as existsSync14 } from "fs";
9865
+ import { rmSync, existsSync as existsSync13 } from "fs";
10437
9866
  import { join as join17 } from "path";
10438
9867
  function clean(options) {
10439
9868
  const root = process.cwd();
@@ -10444,7 +9873,7 @@ function clean(options) {
10444
9873
  const lockfiles = ["bun.lockb", "package-lock.json", "yarn.lock", "pnpm-lock.yaml"];
10445
9874
  for (const dir of dirs) {
10446
9875
  const path = join17(root, dir);
10447
- if (existsSync14(path)) {
9876
+ if (existsSync13(path)) {
10448
9877
  rmSync(path, { recursive: true });
10449
9878
  console.log(source_default.dim(` Removed ${dir}/`));
10450
9879
  }
@@ -10452,7 +9881,7 @@ function clean(options) {
10452
9881
  if (options?.all) {
10453
9882
  for (const file of lockfiles) {
10454
9883
  const path = join17(root, file);
10455
- if (existsSync14(path)) {
9884
+ if (existsSync13(path)) {
10456
9885
  rmSync(path);
10457
9886
  console.log(source_default.dim(` Removed ${file}`));
10458
9887
  }
@@ -10462,15 +9891,8 @@ function clean(options) {
10462
9891
  }
10463
9892
 
10464
9893
  // src/commands/login.ts
10465
- import { createServer } from "http";
9894
+ var ACCOUNTS_SCOPE_URL = "https://my.construct.space/api/accounts/me/scope";
10466
9895
  async function login(options) {
10467
- const portalURL = options?.portal || DEFAULT_PORTAL;
10468
- const fromApp = loadFromApp();
10469
- if (fromApp) {
10470
- console.log(source_default.green(`Using Construct app profile: ${fromApp.user?.name || fromApp.user?.email}`));
10471
- console.log(source_default.dim(" To use a different identity, sign out of the app or run `construct logout` after login."));
10472
- return;
10473
- }
10474
9896
  if (isAuthenticated()) {
10475
9897
  const creds = load2();
10476
9898
  const name = creds.user?.name || "unknown";
@@ -10478,159 +9900,85 @@ async function login(options) {
10478
9900
  console.log(source_default.dim(" Run 'construct logout' to sign out first."));
10479
9901
  return;
10480
9902
  }
10481
- const server = createServer();
10482
- await new Promise((resolve3) => server.listen(0, "127.0.0.1", resolve3));
10483
- const port = server.address().port;
10484
- server.close();
10485
- const callbackURL = `http://localhost:${port}/callback`;
10486
- const loginURL = `${portalURL}/api/auth/cli-login?callback=${encodeURIComponent(callbackURL)}`;
10487
- const tokenPromise = new Promise((resolve3, reject) => {
10488
- const timeout = setTimeout(() => {
10489
- srv.close();
10490
- reject(new Error("Login timed out. Try again."));
10491
- }, 5 * 60 * 1000);
10492
- const srv = createServer((req, res) => {
10493
- const url = new URL(req.url, `http://localhost:${port}`);
10494
- if (url.pathname !== "/callback") {
10495
- res.end();
10496
- return;
10497
- }
10498
- const error2 = url.searchParams.get("error");
10499
- const token = url.searchParams.get("token");
10500
- res.setHeader("Content-Type", "text/html");
10501
- if (error2) {
10502
- res.end(`<html><body style="font-family:system-ui;text-align:center;padding:60px">
10503
- <h2 style="color:#EF4444">Login Failed</h2><p>${error2}</p>
10504
- <p style="color:#6B7280">You can close this window.</p></body></html>`);
10505
- clearTimeout(timeout);
10506
- srv.close();
10507
- reject(new Error(error2));
10508
- return;
10509
- }
10510
- if (!token) {
10511
- res.end(`<html><body style="font-family:system-ui;text-align:center;padding:60px">
10512
- <h2 style="color:#EF4444">Login Failed</h2><p>No token received.</p>
10513
- <p style="color:#6B7280">You can close this window.</p></body></html>`);
10514
- clearTimeout(timeout);
10515
- srv.close();
10516
- reject(new Error("No token received"));
10517
- return;
10518
- }
10519
- res.end(`<html><body style="font-family:system-ui;text-align:center;padding:60px">
10520
- <h2 style="color:#10B981">Logged In!</h2>
10521
- <p>You can close this window and return to your terminal.</p></body></html>`);
10522
- clearTimeout(timeout);
10523
- srv.close();
10524
- resolve3(token);
10525
- });
10526
- srv.listen(port, "127.0.0.1");
10527
- });
10528
- console.log(source_default.blue("Opening browser to log in..."));
10529
- console.log(source_default.dim(" If the browser doesn't open, visit:"));
10530
- console.log(source_default.dim(` ${loginURL}`));
9903
+ if (options?.token) {
9904
+ await loginWithToken(options.token);
9905
+ return;
9906
+ }
9907
+ const profiles = listDesktopProfiles();
9908
+ if (profiles.length > 0) {
9909
+ await loginFromProfile(profiles);
9910
+ return;
9911
+ }
9912
+ console.log(source_default.yellow("No signed-in profiles found on this machine."));
10531
9913
  console.log();
10532
- openBrowser(loginURL);
10533
- console.log(source_default.dim(" Waiting for authentication..."));
9914
+ console.log(" Two options:");
9915
+ console.log(" 1. Sign in with the Construct desktop app and re-run this command, or");
9916
+ console.log(` 2. Paste a token from ${source_default.cyan("https://my.construct.space/settings/tokens")} below.`);
9917
+ console.log();
9918
+ const token = await dist_default5({
9919
+ message: "Token (or press Ctrl+C to cancel):",
9920
+ validate: (s) => s.trim().length > 0 || "Token required"
9921
+ });
9922
+ await loginWithToken(token.trim());
9923
+ }
9924
+ async function loginFromProfile(profiles) {
9925
+ let picked = profiles[0];
9926
+ if (profiles.length > 1) {
9927
+ picked = await dist_default6({
9928
+ message: "Choose a profile to use:",
9929
+ choices: profiles.map((p) => ({ name: formatLabel(p), value: p }))
9930
+ });
9931
+ }
9932
+ const user = {
9933
+ id: picked.user?.id || picked.id,
9934
+ name: picked.user?.name || picked.user?.username || picked.id,
9935
+ email: picked.user?.email || ""
9936
+ };
9937
+ store({ token: picked.token, portal: DEFAULT_PORTAL, user });
9938
+ console.log(source_default.green(`Logged in as ${user.name}`));
9939
+ if (user.email)
9940
+ console.log(source_default.dim(` ${user.email}`));
9941
+ console.log(source_default.dim(` profile: ${picked.id}`));
9942
+ }
9943
+ async function loginWithToken(token) {
9944
+ let resp;
10534
9945
  try {
10535
- const token = await tokenPromise;
10536
- const resp = await fetch(`${portalURL}/api/auth/cli-verify`, {
9946
+ resp = await fetch(ACCOUNTS_SCOPE_URL, {
10537
9947
  headers: { Authorization: `Bearer ${token}` }
10538
9948
  });
10539
- const { user, publishers } = await resp.json();
10540
- store({ token, portal: portalURL, user, publishers });
10541
- console.log();
10542
- console.log(source_default.green(`Logged in as ${user?.name || "there"}`));
10543
- const list = publishers || [];
10544
- if (list.length === 0) {
10545
- console.log(source_default.yellow(" No publisher yet. Run developer enrollment to publish spaces."));
10546
- } else {
10547
- for (const p of list) {
10548
- const label = p.kind === "org" ? source_default.cyan("org") : p.kind === "user" ? source_default.blue("personal") : source_default.dim("legacy");
10549
- console.log(source_default.dim(` Publisher: ${p.name} (${label})`));
10550
- }
10551
- }
10552
9949
  } catch (err) {
10553
- console.error(source_default.red(`Login failed: ${err.message}`));
9950
+ console.error(source_default.red(`Failed to reach accounts service: ${err.message}`));
10554
9951
  process.exit(1);
10555
9952
  }
10556
- }
10557
- function logout() {
10558
- const wasCli = isAuthenticated();
10559
- clear();
10560
- if (wasCli) {
10561
- console.log(source_default.green("Logged out of CLI credentials."));
10562
- } else {
10563
- console.log(source_default.dim("No CLI credentials to clear."));
9953
+ if (resp.status === 401) {
9954
+ console.error(source_default.red("Token rejected by accounts service (invalid or expired)."));
9955
+ process.exit(1);
10564
9956
  }
10565
- const appStill = loadFromApp();
10566
- if (appStill) {
10567
- console.log();
10568
- console.log(source_default.yellow("Note: the Construct app is still signed in as ") + source_default.white(appStill.user?.name || appStill.user?.email || ""));
10569
- console.log(source_default.dim(" The CLI will continue to use the app profile. Sign out of the app to fully disconnect."));
9957
+ if (!resp.ok) {
9958
+ console.error(source_default.red(`Token verification failed (HTTP ${resp.status}).`));
9959
+ process.exit(1);
10570
9960
  }
9961
+ const body = await resp.json();
9962
+ const u = body.user || {};
9963
+ const user = {
9964
+ id: u.id || u.uuid || "",
9965
+ name: u.name || u.username || "",
9966
+ email: u.email || ""
9967
+ };
9968
+ store({ token, portal: DEFAULT_PORTAL, user });
9969
+ console.log(source_default.green(`Logged in as ${user.name || "unknown"}`));
9970
+ if (user.email)
9971
+ console.log(source_default.dim(` ${user.email}`));
10571
9972
  }
10572
-
10573
- // src/commands/whoami.ts
10574
- async function whoami() {
10575
- const fromApp = loadFromApp();
10576
- let creds = null;
10577
- let source = "none";
10578
- if (fromApp) {
10579
- creds = fromApp;
10580
- source = "app";
10581
- } else {
10582
- try {
10583
- creds = load2();
10584
- source = "cli";
10585
- } catch {
10586
- source = "none";
10587
- }
10588
- }
10589
- if (source === "none" || !creds) {
10590
- console.log(source_default.yellow("Not signed in."));
10591
- console.log(source_default.dim(" Run ") + source_default.white("construct login") + source_default.dim(" or sign in to the Construct app."));
10592
- return;
10593
- }
10594
- const user = creds.user;
10595
- const sourceLabel = source === "app" ? source_default.cyan("Construct app profile") : source_default.blue("CLI login");
10596
- console.log();
10597
- console.log(source_default.bold(user?.name || "Signed in"));
10598
- console.log(source_default.dim(" " + (user?.email || "")));
10599
- console.log(source_default.dim(" id: ") + source_default.dim(user?.id || "\u2014"));
10600
- console.log(source_default.dim(" source: ") + sourceLabel);
10601
- console.log(source_default.dim(" portal: ") + (creds.portal || DEFAULT_PORTAL));
10602
- try {
10603
- const resp = await fetch(`${creds.portal || DEFAULT_PORTAL}/api/auth/cli-verify`, {
10604
- headers: { Authorization: `Bearer ${creds.token}` }
10605
- });
10606
- if (!resp.ok) {
10607
- console.log(source_default.dim(" publishers: ") + source_default.red(`error (${resp.status})`));
10608
- return;
10609
- }
10610
- const body = await resp.json();
10611
- const list = body.publishers || [];
10612
- if (list.length === 0) {
10613
- console.log(source_default.dim(" publishers: ") + source_default.yellow("none yet \u2014 enroll at developer.construct.space"));
10614
- return;
10615
- }
10616
- console.log();
10617
- console.log(source_default.bold("Publishers"));
10618
- for (const p of list) {
10619
- const kindLabel = p.kind === "org" ? source_default.cyan("org") : p.kind === "user" ? source_default.blue("personal") : source_default.dim("legacy");
10620
- const verified = p.verified ? source_default.green(" \u2713") : "";
10621
- console.log(` ${source_default.white(p.name)} (${kindLabel})${verified}`);
10622
- if (p.orgId)
10623
- console.log(source_default.dim(` org: ${p.orgId}`));
10624
- }
10625
- } catch (err) {
10626
- console.log(source_default.dim(" publishers: ") + source_default.red("could not reach portal"));
10627
- }
10628
- console.log();
10629
- if (source === "app") {
10630
- console.log(source_default.dim("To switch identities, change the active profile in the Construct app."));
10631
- } else {
10632
- console.log(source_default.dim("To switch identities, run 'construct logout' then 'construct login'."));
10633
- }
9973
+ function formatLabel(p) {
9974
+ const name = p.user?.name || p.user?.username || p.id;
9975
+ const email = p.user?.email;
9976
+ const kind = p.id.startsWith("org:") ? " [org]" : "";
9977
+ return email ? `${name}${kind} ${source_default.dim(email)}` : `${name}${kind} ${source_default.dim(p.id)}`;
9978
+ }
9979
+ function logout() {
9980
+ clear();
9981
+ console.log(source_default.green("Logged out."));
10634
9982
  }
10635
9983
 
10636
9984
  // src/commands/update.ts
@@ -10662,7 +10010,7 @@ function update() {
10662
10010
  }
10663
10011
 
10664
10012
  // src/commands/graph/init.ts
10665
- import { existsSync as existsSync15, readFileSync as readFileSync11, writeFileSync as writeFileSync7, mkdirSync as mkdirSync6 } from "fs";
10013
+ import { existsSync as existsSync14, readFileSync as readFileSync10, writeFileSync as writeFileSync7, mkdirSync as mkdirSync5 } from "fs";
10666
10014
  import { join as join18 } from "path";
10667
10015
  import { execSync as execSync6 } from "child_process";
10668
10016
  function graphInit() {
@@ -10674,9 +10022,9 @@ function graphInit() {
10674
10022
  }
10675
10023
  const m = read(root);
10676
10024
  const modelsDir = join18(root, "src", "models");
10677
- mkdirSync6(modelsDir, { recursive: true });
10025
+ mkdirSync5(modelsDir, { recursive: true });
10678
10026
  const indexPath = join18(modelsDir, "index.ts");
10679
- if (!existsSync15(indexPath)) {
10027
+ if (!existsSync14(indexPath)) {
10680
10028
  writeFileSync7(indexPath, `// Data models for ${m.name}
10681
10029
  // Generated by construct graph init
10682
10030
 
@@ -10685,7 +10033,7 @@ function graphInit() {
10685
10033
  `);
10686
10034
  }
10687
10035
  const pkgPath = join18(root, "package.json");
10688
- const pkg = JSON.parse(readFileSync11(pkgPath, "utf-8"));
10036
+ const pkg = JSON.parse(readFileSync10(pkgPath, "utf-8"));
10689
10037
  if (!pkg.dependencies)
10690
10038
  pkg.dependencies = {};
10691
10039
  if (!pkg.dependencies["@construct-space/graph"]) {
@@ -10715,7 +10063,7 @@ function graphInit() {
10715
10063
  }
10716
10064
 
10717
10065
  // src/commands/graph/generate.ts
10718
- import { existsSync as existsSync16, readFileSync as readFileSync12, writeFileSync as writeFileSync8, mkdirSync as mkdirSync7 } from "fs";
10066
+ import { existsSync as existsSync15, readFileSync as readFileSync11, writeFileSync as writeFileSync8, mkdirSync as mkdirSync6 } from "fs";
10719
10067
  import { join as join19 } from "path";
10720
10068
  var FIELD_TYPES = {
10721
10069
  string: "field.string()",
@@ -10869,9 +10217,9 @@ function generate2(modelName, fieldSpecs, options) {
10869
10217
  const content = lines.join(`
10870
10218
  `);
10871
10219
  const modelsDir = join19(root, "src", "models");
10872
- mkdirSync7(modelsDir, { recursive: true });
10220
+ mkdirSync6(modelsDir, { recursive: true });
10873
10221
  const filePath = join19(modelsDir, `${name}.ts`);
10874
- if (existsSync16(filePath)) {
10222
+ if (existsSync15(filePath)) {
10875
10223
  console.log(source_default.yellow(` Model file already exists: src/models/${name}.ts`));
10876
10224
  console.log(source_default.dim(" Overwriting..."));
10877
10225
  }
@@ -10890,8 +10238,8 @@ function generate2(modelName, fieldSpecs, options) {
10890
10238
  function updateBarrel(modelsDir, modelName) {
10891
10239
  const indexPath = join19(modelsDir, "index.ts");
10892
10240
  const exportLine = `export { ${modelName} } from './${modelName}'`;
10893
- if (existsSync16(indexPath)) {
10894
- const content = readFileSync12(indexPath, "utf-8");
10241
+ if (existsSync15(indexPath)) {
10242
+ const content = readFileSync11(indexPath, "utf-8");
10895
10243
  if (content.includes(exportLine))
10896
10244
  return;
10897
10245
  writeFileSync8(indexPath, content.trimEnd() + `
@@ -10905,7 +10253,7 @@ function updateBarrel(modelsDir, modelName) {
10905
10253
  }
10906
10254
 
10907
10255
  // src/commands/graph/push.ts
10908
- import { existsSync as existsSync17, readdirSync as readdirSync4, readFileSync as readFileSync13 } from "fs";
10256
+ import { existsSync as existsSync16, readdirSync as readdirSync6, readFileSync as readFileSync12 } from "fs";
10909
10257
  import { join as join20, basename as basename7 } from "path";
10910
10258
  async function graphPush() {
10911
10259
  const root = process.cwd();
@@ -10915,11 +10263,11 @@ async function graphPush() {
10915
10263
  }
10916
10264
  const m = read(root);
10917
10265
  const modelsDir = join20(root, "src", "models");
10918
- if (!existsSync17(modelsDir)) {
10266
+ if (!existsSync16(modelsDir)) {
10919
10267
  console.error(source_default.red("No src/models/ directory found. Run 'construct graph init' first."));
10920
10268
  process.exit(1);
10921
10269
  }
10922
- const modelFiles = readdirSync4(modelsDir).filter((f) => f.endsWith(".ts") && f !== "index.ts");
10270
+ const modelFiles = readdirSync6(modelsDir).filter((f) => f.endsWith(".ts") && f !== "index.ts");
10923
10271
  if (modelFiles.length === 0) {
10924
10272
  console.error(source_default.red("No model files found in src/models/"));
10925
10273
  console.log(source_default.dim(" Generate one: construct graph g User name:string email:string"));
@@ -10928,7 +10276,7 @@ async function graphPush() {
10928
10276
  console.log(source_default.blue(`Pushing ${modelFiles.length} model(s) for space: ${m.id}`));
10929
10277
  const models = [];
10930
10278
  for (const file of modelFiles) {
10931
- const content = readFileSync13(join20(modelsDir, file), "utf-8");
10279
+ const content = readFileSync12(join20(modelsDir, file), "utf-8");
10932
10280
  const model = parseModelFile(content, basename7(file, ".ts"));
10933
10281
  if (model)
10934
10282
  models.push(model);
@@ -10944,15 +10292,17 @@ async function graphPush() {
10944
10292
  console.error(source_default.red(err.message));
10945
10293
  process.exit(1);
10946
10294
  }
10947
- const graphURL = process.env.GRAPH_URL || "https://graph.construct.space";
10295
+ const graphURL = process.env.GRAPH_URL || "https://my.construct.space/api/graph";
10948
10296
  const spinner = ora("Registering models...").start();
10949
10297
  try {
10298
+ const userID = creds.user?.id || "";
10950
10299
  const resp = await fetch(`${graphURL}/api/schemas/register`, {
10951
10300
  method: "POST",
10952
10301
  headers: {
10953
10302
  "Content-Type": "application/json",
10954
10303
  Authorization: `Bearer ${creds.token}`,
10955
- "X-Space-ID": m.id
10304
+ "X-Space-ID": m.id,
10305
+ "X-Auth-User-ID": userID
10956
10306
  },
10957
10307
  body: JSON.stringify({
10958
10308
  space_id: m.id,
@@ -10962,6 +10312,20 @@ async function graphPush() {
10962
10312
  manifest: { version: 1, models }
10963
10313
  })
10964
10314
  });
10315
+ if (resp.status === 403) {
10316
+ spinner.fail("Ownership check failed");
10317
+ try {
10318
+ const errBody = await resp.json();
10319
+ console.error(source_default.red(` ${errBody.error || "You are not the owner of this space"}`));
10320
+ if (errBody.owner_user_id) {
10321
+ console.error(source_default.dim(` Current owner: ${errBody.owner_user_id}`));
10322
+ }
10323
+ console.error(source_default.dim(" Fork to a new space_id to publish your own version."));
10324
+ } catch {
10325
+ console.error(source_default.red(` 403: Forbidden \u2014 ownership check failed`));
10326
+ }
10327
+ process.exit(1);
10328
+ }
10965
10329
  if (!resp.ok) {
10966
10330
  const body = await resp.text();
10967
10331
  spinner.fail("Registration failed");
@@ -11044,7 +10408,7 @@ function parseModelFile(content, fileName) {
11044
10408
  }
11045
10409
 
11046
10410
  // src/commands/graph/migrate.ts
11047
- import { existsSync as existsSync18, readdirSync as readdirSync5, readFileSync as readFileSync14 } from "fs";
10411
+ import { existsSync as existsSync17, readdirSync as readdirSync7, readFileSync as readFileSync13 } from "fs";
11048
10412
  import { join as join21, basename as basename8 } from "path";
11049
10413
  async function graphMigrate(options) {
11050
10414
  const root = process.cwd();
@@ -11054,7 +10418,7 @@ async function graphMigrate(options) {
11054
10418
  }
11055
10419
  const m = read(root);
11056
10420
  const modelsDir = join21(root, "src", "models");
11057
- if (!existsSync18(modelsDir)) {
10421
+ if (!existsSync17(modelsDir)) {
11058
10422
  console.error(source_default.red("No src/models/ directory. Run 'construct graph init' first."));
11059
10423
  process.exit(1);
11060
10424
  }
@@ -11065,7 +10429,7 @@ async function graphMigrate(options) {
11065
10429
  console.error(source_default.red(err.message));
11066
10430
  process.exit(1);
11067
10431
  }
11068
- const graphURL = process.env.GRAPH_URL || "https://graph.construct.space";
10432
+ const graphURL = process.env.GRAPH_URL || "https://my.construct.space/api/graph";
11069
10433
  const spinner = ora("Fetching current schema...").start();
11070
10434
  let serverModels = [];
11071
10435
  try {
@@ -11081,10 +10445,10 @@ async function graphMigrate(options) {
11081
10445
  spinner.fail("Could not fetch schema");
11082
10446
  process.exit(1);
11083
10447
  }
11084
- const modelFiles = readdirSync5(modelsDir).filter((f) => f.endsWith(".ts") && f !== "index.ts");
10448
+ const modelFiles = readdirSync7(modelsDir).filter((f) => f.endsWith(".ts") && f !== "index.ts");
11085
10449
  const localModels = [];
11086
10450
  for (const file of modelFiles) {
11087
- const content = readFileSync14(join21(modelsDir, file), "utf-8");
10451
+ const content = readFileSync13(join21(modelsDir, file), "utf-8");
11088
10452
  const model = parseModelFields(content, basename8(file, ".ts"));
11089
10453
  if (model)
11090
10454
  localModels.push(model);
@@ -11188,7 +10552,7 @@ function parseModelFields(content, fileName) {
11188
10552
  }
11189
10553
 
11190
10554
  // src/index.ts
11191
- var VERSION = "1.1.12";
10555
+ var VERSION = "1.3.1";
11192
10556
  var program2 = new Command;
11193
10557
  program2.name("construct").description("Construct CLI \u2014 scaffold, build, develop, and publish spaces").version(VERSION);
11194
10558
  program2.command("scaffold [name]").alias("new").alias("create").description("Create a new Construct space project").option("--with-tests", "Include E2E testing boilerplate").option("--full", "Full preset: multiple pages, extra skills, widget templates").action(async (name, opts) => scaffold(name, opts));
@@ -11199,9 +10563,8 @@ program2.command("publish").description("Publish a space to the Construct regist
11199
10563
  program2.command("validate").description("Validate space.manifest.json").action(() => validate3());
11200
10564
  program2.command("check").description("Run type-check (vue-tsc) and linter (eslint)").action(() => check());
11201
10565
  program2.command("clean").description("Remove build artifacts").option("--all", "Also remove node_modules and lockfiles").action((opts) => clean(opts));
11202
- program2.command("login").description("Authenticate with Construct").option("--portal <url>", "Portal URL").action(async (opts) => login(opts));
10566
+ program2.command("login").description("Authenticate with Construct").option("--portal <url>", "Portal URL").option("--token <token>", "Authenticate with a token directly (skip desktop profile picker)").action(async (opts) => login(opts));
11203
10567
  program2.command("logout").description("Sign out").action(() => logout());
11204
- program2.command("whoami").alias("status").description("Show the active profile, its source (app or CLI), and linked publishers").action(() => whoami());
11205
10568
  program2.command("update").description("Update the CLI to the latest version").action(() => update());
11206
10569
  var graph = program2.command("graph").description("Construct Graph \u2014 data models and GraphQL");
11207
10570
  graph.command("init").description("Initialize Graph in a space project").action(() => graphInit());