@construct-space/cli 1.1.11 → 1.1.12
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 +711 -91
- package/package.json +2 -2
package/dist/index.js
CHANGED
|
@@ -4613,18 +4613,616 @@ function runHook(hooks, hookName, root) {
|
|
|
4613
4613
|
execSync2(cmd, { cwd: root, stdio: "inherit" });
|
|
4614
4614
|
}
|
|
4615
4615
|
|
|
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
|
+
|
|
4616
5210
|
// src/commands/scaffold.ts
|
|
4617
5211
|
var nameRegex = /^[a-z][a-z0-9-]*$/;
|
|
4618
5212
|
function render(template, data) {
|
|
4619
5213
|
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);
|
|
4620
5214
|
}
|
|
4621
5215
|
function writeTemplate(templateDir, tmplName, outPath, data) {
|
|
5216
|
+
let content;
|
|
4622
5217
|
const tmplPath = join2(templateDir, tmplName);
|
|
4623
|
-
if (
|
|
5218
|
+
if (existsSync2(tmplPath)) {
|
|
5219
|
+
content = readFileSync(tmplPath, "utf-8");
|
|
5220
|
+
} else if (EMBEDDED_TEMPLATES[tmplName] !== undefined) {
|
|
5221
|
+
content = EMBEDDED_TEMPLATES[tmplName];
|
|
5222
|
+
} else {
|
|
4624
5223
|
console.warn(source_default.yellow(`Template not found: ${tmplName}`));
|
|
4625
5224
|
return;
|
|
4626
5225
|
}
|
|
4627
|
-
const content = readFileSync(tmplPath, "utf-8");
|
|
4628
5226
|
mkdirSync(dirname(outPath), { recursive: true });
|
|
4629
5227
|
writeFileSync(outPath, render(content, data));
|
|
4630
5228
|
}
|
|
@@ -7662,8 +8260,8 @@ async function build(options) {
|
|
|
7662
8260
|
}
|
|
7663
8261
|
|
|
7664
8262
|
// src/commands/dev.ts
|
|
7665
|
-
import { existsSync as
|
|
7666
|
-
import { join as
|
|
8263
|
+
import { existsSync as existsSync8, readFileSync as readFileSync6, cpSync, mkdirSync as mkdirSync3 } from "fs";
|
|
8264
|
+
import { join as join10 } from "path";
|
|
7667
8265
|
import { createHash as createHash2 } from "crypto";
|
|
7668
8266
|
|
|
7669
8267
|
// node_modules/chokidar/esm/index.js
|
|
@@ -9236,11 +9834,55 @@ function watch(paths, options = {}) {
|
|
|
9236
9834
|
return watcher;
|
|
9237
9835
|
}
|
|
9238
9836
|
|
|
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
|
+
|
|
9239
9881
|
// src/commands/dev.ts
|
|
9240
9882
|
function getEntryWatchPaths(root) {
|
|
9241
9883
|
return [
|
|
9242
|
-
|
|
9243
|
-
|
|
9884
|
+
join10(root, MANIFEST_FILE),
|
|
9885
|
+
join10(root, "src", "actions.ts")
|
|
9244
9886
|
];
|
|
9245
9887
|
}
|
|
9246
9888
|
async function dev() {
|
|
@@ -9279,14 +9921,14 @@ async function dev() {
|
|
|
9279
9921
|
}
|
|
9280
9922
|
console.log(source_default.blue("Actions changed \u2014 entry regenerated"));
|
|
9281
9923
|
});
|
|
9282
|
-
const distDir =
|
|
9283
|
-
const bundleFile =
|
|
9924
|
+
const distDir = join10(root, "dist");
|
|
9925
|
+
const bundleFile = join10(distDir, `space-${m.id}.iife.js`);
|
|
9284
9926
|
let lastChecksum = "";
|
|
9285
9927
|
const distWatcher = watch(bundleFile, { ignoreInitial: false });
|
|
9286
9928
|
distWatcher.on("all", () => {
|
|
9287
|
-
if (!
|
|
9929
|
+
if (!existsSync8(bundleFile))
|
|
9288
9930
|
return;
|
|
9289
|
-
const bundleData =
|
|
9931
|
+
const bundleData = readFileSync6(bundleFile);
|
|
9290
9932
|
const checksum = createHash2("sha256").update(bundleData).digest("hex");
|
|
9291
9933
|
if (checksum === lastChecksum)
|
|
9292
9934
|
return;
|
|
@@ -9298,7 +9940,15 @@ async function dev() {
|
|
|
9298
9940
|
hostApiVersion: "0.2.0",
|
|
9299
9941
|
builtAt: new Date().toISOString()
|
|
9300
9942
|
});
|
|
9301
|
-
|
|
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
|
+
}
|
|
9302
9952
|
});
|
|
9303
9953
|
console.log(source_default.green("Watching for changes... (Ctrl+C to stop)"));
|
|
9304
9954
|
console.log(source_default.dim("Use the Preview button in Construct to open the Space Runner"));
|
|
@@ -9306,38 +9956,8 @@ async function dev() {
|
|
|
9306
9956
|
}
|
|
9307
9957
|
|
|
9308
9958
|
// src/commands/run.ts
|
|
9309
|
-
import { existsSync as
|
|
9959
|
+
import { existsSync as existsSync9, cpSync as cpSync2, mkdirSync as mkdirSync4 } from "fs";
|
|
9310
9960
|
import { join as join11 } from "path";
|
|
9311
|
-
|
|
9312
|
-
// src/lib/appdir.ts
|
|
9313
|
-
import { join as join10 } from "path";
|
|
9314
|
-
import { homedir } from "os";
|
|
9315
|
-
import { platform as platform2 } from "process";
|
|
9316
|
-
function dataDir() {
|
|
9317
|
-
if (process.env.CONSTRUCT_DATA_DIR)
|
|
9318
|
-
return process.env.CONSTRUCT_DATA_DIR;
|
|
9319
|
-
const home = homedir();
|
|
9320
|
-
switch (platform2) {
|
|
9321
|
-
case "darwin":
|
|
9322
|
-
return join10(home, "Library", "Application Support", "Construct");
|
|
9323
|
-
case "win32": {
|
|
9324
|
-
const appData = process.env.APPDATA || join10(home, "AppData", "Roaming");
|
|
9325
|
-
return join10(appData, "Construct");
|
|
9326
|
-
}
|
|
9327
|
-
default: {
|
|
9328
|
-
const xdg = process.env.XDG_DATA_HOME || join10(home, ".local", "share");
|
|
9329
|
-
return join10(xdg, "construct");
|
|
9330
|
-
}
|
|
9331
|
-
}
|
|
9332
|
-
}
|
|
9333
|
-
function spacesDir() {
|
|
9334
|
-
return join10(dataDir(), "spaces");
|
|
9335
|
-
}
|
|
9336
|
-
function spaceDir(spaceId) {
|
|
9337
|
-
return join10(spacesDir(), spaceId);
|
|
9338
|
-
}
|
|
9339
|
-
|
|
9340
|
-
// src/commands/run.ts
|
|
9341
9961
|
function install() {
|
|
9342
9962
|
const root = process.cwd();
|
|
9343
9963
|
if (!exists(root)) {
|
|
@@ -9345,28 +9965,28 @@ function install() {
|
|
|
9345
9965
|
process.exit(1);
|
|
9346
9966
|
}
|
|
9347
9967
|
const distDir = join11(root, "dist");
|
|
9348
|
-
if (!
|
|
9968
|
+
if (!existsSync9(distDir)) {
|
|
9349
9969
|
console.error(source_default.red("No dist/ directory found. Run 'construct build' first."));
|
|
9350
9970
|
process.exit(1);
|
|
9351
9971
|
}
|
|
9352
9972
|
const m = read(root);
|
|
9353
9973
|
const agentDir = join11(root, "agent");
|
|
9354
|
-
if (
|
|
9974
|
+
if (existsSync9(agentDir)) {
|
|
9355
9975
|
bundleAgentDir(agentDir, distDir);
|
|
9356
9976
|
}
|
|
9357
9977
|
const installDir = spaceDir(m.id);
|
|
9358
|
-
|
|
9359
|
-
|
|
9978
|
+
mkdirSync4(installDir, { recursive: true });
|
|
9979
|
+
cpSync2(distDir, installDir, { recursive: true });
|
|
9360
9980
|
console.log(source_default.green(`Installed ${m.name} \u2192 ${installDir}`));
|
|
9361
9981
|
console.log(source_default.dim(" Restart Construct to load the updated space."));
|
|
9362
9982
|
}
|
|
9363
9983
|
|
|
9364
9984
|
// src/commands/publish.ts
|
|
9365
|
-
import { readFileSync as
|
|
9985
|
+
import { readFileSync as readFileSync8, writeFileSync as writeFileSync6, statSync as statSync4, unlinkSync as unlinkSync2 } from "fs";
|
|
9366
9986
|
import { join as join14, basename as basename6 } from "path";
|
|
9367
9987
|
|
|
9368
9988
|
// src/lib/auth.ts
|
|
9369
|
-
import { readFileSync as
|
|
9989
|
+
import { readFileSync as readFileSync7, writeFileSync as writeFileSync5, mkdirSync as mkdirSync5, unlinkSync, existsSync as existsSync10 } from "fs";
|
|
9370
9990
|
import { join as join12, dirname as dirname4 } from "path";
|
|
9371
9991
|
var CREDENTIALS_FILE = "credentials.json";
|
|
9372
9992
|
var APP_PROFILES_FILE = "profiles.json";
|
|
@@ -9383,15 +10003,15 @@ function appProfileAuthPath(profileId) {
|
|
|
9383
10003
|
function loadFromApp() {
|
|
9384
10004
|
try {
|
|
9385
10005
|
const profilesPath = appProfilesPath();
|
|
9386
|
-
if (!
|
|
10006
|
+
if (!existsSync10(profilesPath))
|
|
9387
10007
|
return null;
|
|
9388
|
-
const profiles = JSON.parse(
|
|
10008
|
+
const profiles = JSON.parse(readFileSync7(profilesPath, "utf-8"));
|
|
9389
10009
|
if (!profiles?.active_profile)
|
|
9390
10010
|
return null;
|
|
9391
10011
|
const authPath = appProfileAuthPath(profiles.active_profile);
|
|
9392
|
-
if (!
|
|
10012
|
+
if (!existsSync10(authPath))
|
|
9393
10013
|
return null;
|
|
9394
|
-
const auth = JSON.parse(
|
|
10014
|
+
const auth = JSON.parse(readFileSync7(authPath, "utf-8"));
|
|
9395
10015
|
if (!auth?.authenticated || !auth?.token)
|
|
9396
10016
|
return null;
|
|
9397
10017
|
return {
|
|
@@ -9409,7 +10029,7 @@ function loadFromApp() {
|
|
|
9409
10029
|
}
|
|
9410
10030
|
function store(creds) {
|
|
9411
10031
|
const path = credentialsPath();
|
|
9412
|
-
|
|
10032
|
+
mkdirSync5(dirname4(path), { recursive: true });
|
|
9413
10033
|
writeFileSync5(path, JSON.stringify(creds, null, 2) + `
|
|
9414
10034
|
`, { mode: 384 });
|
|
9415
10035
|
}
|
|
@@ -9418,10 +10038,10 @@ function load2() {
|
|
|
9418
10038
|
if (fromApp)
|
|
9419
10039
|
return fromApp;
|
|
9420
10040
|
const path = credentialsPath();
|
|
9421
|
-
if (!
|
|
10041
|
+
if (!existsSync10(path)) {
|
|
9422
10042
|
throw new Error("not logged in \u2014 run 'construct login' first (or sign in to the Construct app)");
|
|
9423
10043
|
}
|
|
9424
|
-
const data = JSON.parse(
|
|
10044
|
+
const data = JSON.parse(readFileSync7(path, "utf-8"));
|
|
9425
10045
|
if (!data.token) {
|
|
9426
10046
|
throw new Error("not logged in \u2014 run 'construct login' first");
|
|
9427
10047
|
}
|
|
@@ -9437,12 +10057,12 @@ function isAuthenticated() {
|
|
|
9437
10057
|
}
|
|
9438
10058
|
function clear() {
|
|
9439
10059
|
const path = credentialsPath();
|
|
9440
|
-
if (
|
|
10060
|
+
if (existsSync10(path))
|
|
9441
10061
|
unlinkSync(path);
|
|
9442
10062
|
}
|
|
9443
10063
|
|
|
9444
10064
|
// src/lib/pack.ts
|
|
9445
|
-
import { readdirSync as readdirSync3, statSync as statSync3, existsSync as
|
|
10065
|
+
import { readdirSync as readdirSync3, statSync as statSync3, existsSync as existsSync11 } from "fs";
|
|
9446
10066
|
import { join as join13 } from "path";
|
|
9447
10067
|
import { tmpdir } from "os";
|
|
9448
10068
|
import { execSync as execSync3 } from "child_process";
|
|
@@ -9481,7 +10101,7 @@ async function packSource(root) {
|
|
|
9481
10101
|
const tarballPath = join13(tmpdir(), `space-source-${Date.now()}.tar.gz`);
|
|
9482
10102
|
const entries = [];
|
|
9483
10103
|
for (const name of allowedRootFiles) {
|
|
9484
|
-
if (
|
|
10104
|
+
if (existsSync11(join13(root, name)))
|
|
9485
10105
|
entries.push(name);
|
|
9486
10106
|
}
|
|
9487
10107
|
for (const entry of readdirSync3(root)) {
|
|
@@ -9495,10 +10115,10 @@ async function packSource(root) {
|
|
|
9495
10115
|
entries.push(entry);
|
|
9496
10116
|
}
|
|
9497
10117
|
for (const dir of allowedDirs) {
|
|
9498
|
-
if (
|
|
10118
|
+
if (existsSync11(join13(root, dir)))
|
|
9499
10119
|
entries.push(dir);
|
|
9500
10120
|
}
|
|
9501
|
-
const validEntries = entries.filter((e) =>
|
|
10121
|
+
const validEntries = entries.filter((e) => existsSync11(join13(root, e)));
|
|
9502
10122
|
if (validEntries.length === 0) {
|
|
9503
10123
|
throw new Error("No files to pack");
|
|
9504
10124
|
}
|
|
@@ -9516,7 +10136,7 @@ async function packSource(root) {
|
|
|
9516
10136
|
async function uploadSource(portalURL, token, tarballPath, m) {
|
|
9517
10137
|
const formData = new FormData;
|
|
9518
10138
|
formData.append("manifest", JSON.stringify(m));
|
|
9519
|
-
const fileData =
|
|
10139
|
+
const fileData = readFileSync8(tarballPath);
|
|
9520
10140
|
const blob = new Blob([fileData]);
|
|
9521
10141
|
formData.append("source", blob, basename6(tarballPath));
|
|
9522
10142
|
const resp = await fetch(`${portalURL}/api/publish`, {
|
|
@@ -9540,7 +10160,7 @@ function setVersionInFiles(root, oldVer, newVer) {
|
|
|
9540
10160
|
for (const file of ["package.json", "space.manifest.json"]) {
|
|
9541
10161
|
const path = join14(root, file);
|
|
9542
10162
|
try {
|
|
9543
|
-
const data =
|
|
10163
|
+
const data = readFileSync8(path, "utf-8");
|
|
9544
10164
|
writeFileSync6(path, data.replace(oldStr, newStr));
|
|
9545
10165
|
} catch {}
|
|
9546
10166
|
}
|
|
@@ -9702,7 +10322,7 @@ async function publish(options) {
|
|
|
9702
10322
|
}
|
|
9703
10323
|
|
|
9704
10324
|
// src/commands/validate.ts
|
|
9705
|
-
import { existsSync as
|
|
10325
|
+
import { existsSync as existsSync12, readFileSync as readFileSync9 } from "fs";
|
|
9706
10326
|
import { join as join15 } from "path";
|
|
9707
10327
|
function validate3() {
|
|
9708
10328
|
const root = process.cwd();
|
|
@@ -9722,21 +10342,21 @@ function validate3() {
|
|
|
9722
10342
|
for (const page of m.pages) {
|
|
9723
10343
|
const component = page.component || (page.path === "" ? "pages/index.vue" : `pages/${page.path}.vue`);
|
|
9724
10344
|
const fullPath = join15(root, "src", component);
|
|
9725
|
-
if (!
|
|
10345
|
+
if (!existsSync12(fullPath)) {
|
|
9726
10346
|
console.log(source_default.yellow(` \u26A0 Page component not found: src/${component}`));
|
|
9727
10347
|
warnings++;
|
|
9728
10348
|
}
|
|
9729
10349
|
}
|
|
9730
10350
|
if (m.agent) {
|
|
9731
10351
|
const agentPath = join15(root, m.agent);
|
|
9732
|
-
if (!
|
|
10352
|
+
if (!existsSync12(agentPath)) {
|
|
9733
10353
|
console.log(source_default.yellow(` \u26A0 Agent config not found: ${m.agent}`));
|
|
9734
10354
|
warnings++;
|
|
9735
10355
|
}
|
|
9736
10356
|
}
|
|
9737
10357
|
const pkgPath = join15(root, "package.json");
|
|
9738
|
-
if (
|
|
9739
|
-
const pkg = JSON.parse(
|
|
10358
|
+
if (existsSync12(pkgPath)) {
|
|
10359
|
+
const pkg = JSON.parse(readFileSync9(pkgPath, "utf-8"));
|
|
9740
10360
|
if (pkg.version && pkg.version !== m.version) {
|
|
9741
10361
|
console.log(source_default.yellow(` \u26A0 Version mismatch: manifest=${m.version} package.json=${pkg.version}`));
|
|
9742
10362
|
warnings++;
|
|
@@ -9751,7 +10371,7 @@ function validate3() {
|
|
|
9751
10371
|
|
|
9752
10372
|
// src/commands/check.ts
|
|
9753
10373
|
import { execSync as execSync4 } from "child_process";
|
|
9754
|
-
import { existsSync as
|
|
10374
|
+
import { existsSync as existsSync13, readFileSync as readFileSync10 } from "fs";
|
|
9755
10375
|
import { join as join16 } from "path";
|
|
9756
10376
|
function check() {
|
|
9757
10377
|
const root = process.cwd();
|
|
@@ -9771,18 +10391,18 @@ function check() {
|
|
|
9771
10391
|
let warnings = 0;
|
|
9772
10392
|
for (const page of m.pages) {
|
|
9773
10393
|
const component = page.component || (page.path === "" ? "pages/index.vue" : `pages/${page.path}.vue`);
|
|
9774
|
-
if (!
|
|
10394
|
+
if (!existsSync13(join16(root, "src", component))) {
|
|
9775
10395
|
console.log(source_default.yellow(` \u26A0 Page not found: src/${component}`));
|
|
9776
10396
|
warnings++;
|
|
9777
10397
|
}
|
|
9778
10398
|
}
|
|
9779
|
-
if (m.agent && !
|
|
10399
|
+
if (m.agent && !existsSync13(join16(root, m.agent))) {
|
|
9780
10400
|
console.log(source_default.yellow(` \u26A0 Agent config not found: ${m.agent}`));
|
|
9781
10401
|
warnings++;
|
|
9782
10402
|
}
|
|
9783
10403
|
const pkgPath = join16(root, "package.json");
|
|
9784
|
-
if (
|
|
9785
|
-
const pkg = JSON.parse(
|
|
10404
|
+
if (existsSync13(pkgPath)) {
|
|
10405
|
+
const pkg = JSON.parse(readFileSync10(pkgPath, "utf-8"));
|
|
9786
10406
|
if (pkg.version && pkg.version !== m.version) {
|
|
9787
10407
|
console.log(source_default.yellow(` \u26A0 Version mismatch: manifest=${m.version} package.json=${pkg.version}`));
|
|
9788
10408
|
warnings++;
|
|
@@ -9813,7 +10433,7 @@ function check() {
|
|
|
9813
10433
|
}
|
|
9814
10434
|
|
|
9815
10435
|
// src/commands/clean.ts
|
|
9816
|
-
import { rmSync, existsSync as
|
|
10436
|
+
import { rmSync, existsSync as existsSync14 } from "fs";
|
|
9817
10437
|
import { join as join17 } from "path";
|
|
9818
10438
|
function clean(options) {
|
|
9819
10439
|
const root = process.cwd();
|
|
@@ -9824,7 +10444,7 @@ function clean(options) {
|
|
|
9824
10444
|
const lockfiles = ["bun.lockb", "package-lock.json", "yarn.lock", "pnpm-lock.yaml"];
|
|
9825
10445
|
for (const dir of dirs) {
|
|
9826
10446
|
const path = join17(root, dir);
|
|
9827
|
-
if (
|
|
10447
|
+
if (existsSync14(path)) {
|
|
9828
10448
|
rmSync(path, { recursive: true });
|
|
9829
10449
|
console.log(source_default.dim(` Removed ${dir}/`));
|
|
9830
10450
|
}
|
|
@@ -9832,7 +10452,7 @@ function clean(options) {
|
|
|
9832
10452
|
if (options?.all) {
|
|
9833
10453
|
for (const file of lockfiles) {
|
|
9834
10454
|
const path = join17(root, file);
|
|
9835
|
-
if (
|
|
10455
|
+
if (existsSync14(path)) {
|
|
9836
10456
|
rmSync(path);
|
|
9837
10457
|
console.log(source_default.dim(` Removed ${file}`));
|
|
9838
10458
|
}
|
|
@@ -10042,7 +10662,7 @@ function update() {
|
|
|
10042
10662
|
}
|
|
10043
10663
|
|
|
10044
10664
|
// src/commands/graph/init.ts
|
|
10045
|
-
import { existsSync as
|
|
10665
|
+
import { existsSync as existsSync15, readFileSync as readFileSync11, writeFileSync as writeFileSync7, mkdirSync as mkdirSync6 } from "fs";
|
|
10046
10666
|
import { join as join18 } from "path";
|
|
10047
10667
|
import { execSync as execSync6 } from "child_process";
|
|
10048
10668
|
function graphInit() {
|
|
@@ -10054,9 +10674,9 @@ function graphInit() {
|
|
|
10054
10674
|
}
|
|
10055
10675
|
const m = read(root);
|
|
10056
10676
|
const modelsDir = join18(root, "src", "models");
|
|
10057
|
-
|
|
10677
|
+
mkdirSync6(modelsDir, { recursive: true });
|
|
10058
10678
|
const indexPath = join18(modelsDir, "index.ts");
|
|
10059
|
-
if (!
|
|
10679
|
+
if (!existsSync15(indexPath)) {
|
|
10060
10680
|
writeFileSync7(indexPath, `// Data models for ${m.name}
|
|
10061
10681
|
// Generated by construct graph init
|
|
10062
10682
|
|
|
@@ -10065,7 +10685,7 @@ function graphInit() {
|
|
|
10065
10685
|
`);
|
|
10066
10686
|
}
|
|
10067
10687
|
const pkgPath = join18(root, "package.json");
|
|
10068
|
-
const pkg = JSON.parse(
|
|
10688
|
+
const pkg = JSON.parse(readFileSync11(pkgPath, "utf-8"));
|
|
10069
10689
|
if (!pkg.dependencies)
|
|
10070
10690
|
pkg.dependencies = {};
|
|
10071
10691
|
if (!pkg.dependencies["@construct-space/graph"]) {
|
|
@@ -10095,7 +10715,7 @@ function graphInit() {
|
|
|
10095
10715
|
}
|
|
10096
10716
|
|
|
10097
10717
|
// src/commands/graph/generate.ts
|
|
10098
|
-
import { existsSync as
|
|
10718
|
+
import { existsSync as existsSync16, readFileSync as readFileSync12, writeFileSync as writeFileSync8, mkdirSync as mkdirSync7 } from "fs";
|
|
10099
10719
|
import { join as join19 } from "path";
|
|
10100
10720
|
var FIELD_TYPES = {
|
|
10101
10721
|
string: "field.string()",
|
|
@@ -10249,9 +10869,9 @@ function generate2(modelName, fieldSpecs, options) {
|
|
|
10249
10869
|
const content = lines.join(`
|
|
10250
10870
|
`);
|
|
10251
10871
|
const modelsDir = join19(root, "src", "models");
|
|
10252
|
-
|
|
10872
|
+
mkdirSync7(modelsDir, { recursive: true });
|
|
10253
10873
|
const filePath = join19(modelsDir, `${name}.ts`);
|
|
10254
|
-
if (
|
|
10874
|
+
if (existsSync16(filePath)) {
|
|
10255
10875
|
console.log(source_default.yellow(` Model file already exists: src/models/${name}.ts`));
|
|
10256
10876
|
console.log(source_default.dim(" Overwriting..."));
|
|
10257
10877
|
}
|
|
@@ -10270,8 +10890,8 @@ function generate2(modelName, fieldSpecs, options) {
|
|
|
10270
10890
|
function updateBarrel(modelsDir, modelName) {
|
|
10271
10891
|
const indexPath = join19(modelsDir, "index.ts");
|
|
10272
10892
|
const exportLine = `export { ${modelName} } from './${modelName}'`;
|
|
10273
|
-
if (
|
|
10274
|
-
const content =
|
|
10893
|
+
if (existsSync16(indexPath)) {
|
|
10894
|
+
const content = readFileSync12(indexPath, "utf-8");
|
|
10275
10895
|
if (content.includes(exportLine))
|
|
10276
10896
|
return;
|
|
10277
10897
|
writeFileSync8(indexPath, content.trimEnd() + `
|
|
@@ -10285,7 +10905,7 @@ function updateBarrel(modelsDir, modelName) {
|
|
|
10285
10905
|
}
|
|
10286
10906
|
|
|
10287
10907
|
// src/commands/graph/push.ts
|
|
10288
|
-
import { existsSync as
|
|
10908
|
+
import { existsSync as existsSync17, readdirSync as readdirSync4, readFileSync as readFileSync13 } from "fs";
|
|
10289
10909
|
import { join as join20, basename as basename7 } from "path";
|
|
10290
10910
|
async function graphPush() {
|
|
10291
10911
|
const root = process.cwd();
|
|
@@ -10295,7 +10915,7 @@ async function graphPush() {
|
|
|
10295
10915
|
}
|
|
10296
10916
|
const m = read(root);
|
|
10297
10917
|
const modelsDir = join20(root, "src", "models");
|
|
10298
|
-
if (!
|
|
10918
|
+
if (!existsSync17(modelsDir)) {
|
|
10299
10919
|
console.error(source_default.red("No src/models/ directory found. Run 'construct graph init' first."));
|
|
10300
10920
|
process.exit(1);
|
|
10301
10921
|
}
|
|
@@ -10308,7 +10928,7 @@ async function graphPush() {
|
|
|
10308
10928
|
console.log(source_default.blue(`Pushing ${modelFiles.length} model(s) for space: ${m.id}`));
|
|
10309
10929
|
const models = [];
|
|
10310
10930
|
for (const file of modelFiles) {
|
|
10311
|
-
const content =
|
|
10931
|
+
const content = readFileSync13(join20(modelsDir, file), "utf-8");
|
|
10312
10932
|
const model = parseModelFile(content, basename7(file, ".ts"));
|
|
10313
10933
|
if (model)
|
|
10314
10934
|
models.push(model);
|
|
@@ -10424,7 +11044,7 @@ function parseModelFile(content, fileName) {
|
|
|
10424
11044
|
}
|
|
10425
11045
|
|
|
10426
11046
|
// src/commands/graph/migrate.ts
|
|
10427
|
-
import { existsSync as
|
|
11047
|
+
import { existsSync as existsSync18, readdirSync as readdirSync5, readFileSync as readFileSync14 } from "fs";
|
|
10428
11048
|
import { join as join21, basename as basename8 } from "path";
|
|
10429
11049
|
async function graphMigrate(options) {
|
|
10430
11050
|
const root = process.cwd();
|
|
@@ -10434,7 +11054,7 @@ async function graphMigrate(options) {
|
|
|
10434
11054
|
}
|
|
10435
11055
|
const m = read(root);
|
|
10436
11056
|
const modelsDir = join21(root, "src", "models");
|
|
10437
|
-
if (!
|
|
11057
|
+
if (!existsSync18(modelsDir)) {
|
|
10438
11058
|
console.error(source_default.red("No src/models/ directory. Run 'construct graph init' first."));
|
|
10439
11059
|
process.exit(1);
|
|
10440
11060
|
}
|
|
@@ -10464,7 +11084,7 @@ async function graphMigrate(options) {
|
|
|
10464
11084
|
const modelFiles = readdirSync5(modelsDir).filter((f) => f.endsWith(".ts") && f !== "index.ts");
|
|
10465
11085
|
const localModels = [];
|
|
10466
11086
|
for (const file of modelFiles) {
|
|
10467
|
-
const content =
|
|
11087
|
+
const content = readFileSync14(join21(modelsDir, file), "utf-8");
|
|
10468
11088
|
const model = parseModelFields(content, basename8(file, ".ts"));
|
|
10469
11089
|
if (model)
|
|
10470
11090
|
localModels.push(model);
|
|
@@ -10568,7 +11188,7 @@ function parseModelFields(content, fileName) {
|
|
|
10568
11188
|
}
|
|
10569
11189
|
|
|
10570
11190
|
// src/index.ts
|
|
10571
|
-
var VERSION = "1.1.
|
|
11191
|
+
var VERSION = "1.1.12";
|
|
10572
11192
|
var program2 = new Command;
|
|
10573
11193
|
program2.name("construct").description("Construct CLI \u2014 scaffold, build, develop, and publish spaces").version(VERSION);
|
|
10574
11194
|
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));
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@construct-space/cli",
|
|
3
|
-
"version": "1.1.
|
|
3
|
+
"version": "1.1.12",
|
|
4
4
|
"description": "Construct CLI — scaffold, build, develop, and publish spaces",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"bin": {
|
|
@@ -16,7 +16,7 @@
|
|
|
16
16
|
"templates"
|
|
17
17
|
],
|
|
18
18
|
"scripts": {
|
|
19
|
-
"build": "bun build src/index.ts --outdir dist --target bun --format esm && cp -r templates dist/",
|
|
19
|
+
"build": "bun scripts/embed-templates.ts && bun build src/index.ts --outdir dist --target bun --format esm && cp -r templates dist/",
|
|
20
20
|
"dev": "bun run src/index.ts",
|
|
21
21
|
"test": "bun test",
|
|
22
22
|
"typecheck": "tsc --noEmit"
|